Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Babel 将 generator 编译成了什么(个人向笔记) #6

Open
Amybiubiu opened this issue Feb 10, 2021 · 1 comment
Open

Babel 将 generator 编译成了什么(个人向笔记) #6

Amybiubiu opened this issue Feb 10, 2021 · 1 comment
Labels

Comments

@Amybiubiu
Copy link
Owner

Amybiubiu commented Feb 10, 2021

完整代码

var _marked = regeneratorRuntime.mark(helloWorldGenerator);

function helloWorldGenerator() {
    // 1. wrap 之后返回一个 generator 具有原型上继承有 next 方法
    return regeneratorRuntime.wrap(
        function helloWorldGenerator$(_context) {
            while (1) {
                switch ((_context.prev = _context.next)) {
                    case 0:
                        _context.next = 2;
                        return "hello";

                    case 2:
                        _context.next = 4;
                        return "world";

                    case 4:
                        return _context.abrupt("return", "ending");

                    case 5:
                    case "end":
                        // 8. 最后一次直接执行 stop 没有参数传入,value 为 undefined
                        return _context.stop();
                }
            }
        },
        _marked,
        this
    );
}
// function* helloWorldGenerator() {
//     yield 'hello';
//     yield 'world';
//     return 'ending';
// }
var hw = helloWorldGenerator();

console.log(hw.next());
console.log(hw.next());
console.log(hw.next());
console.log(hw.next());

调用的时候关键的是通过 wrap 函数返回一个 函数,进入 wrap 函数查看,

  • wrap 函数
   function wrap(innerFn, outerFn, self) {
        var generator = Object.create(outerFn.prototype);

        var context = {
            done: false,
            method: "next",
            next: 0,
            prev: 0,
            // 6. 'ending' 作为 arg 传入
            abrupt: function (type, arg) {
                var record = {};
                record.type = type;
                record.arg = arg;

                return this.complete(record);
            }, 
            complete: function (record, afterLoc) {
                if (record.type === "return") {
                    this.rval = this.arg = record.arg;
                    this.method = "return";
                    this.next = "end";
                }

                return ContinueSentinel;
            },
            stop: function () {
                this.done = true;
                // 7. 第三次的 next 最终返回值在此处,返回给 70 行 record arg
                return this.rval;
            }
        };

        generator._invoke = makeInvokeMethod(innerFn, context);

        return generator;
    }

wrap 函数返回了一个 generator 函数,该函数有继承自 outerFn 的 next 方法。这也是 next 能被执行的原因。

  • 再看 mark 函数和 makeInvokeMethod
var mark = function (genFun) {
        var generator = Object.create({
            next: function (arg) {
                // 关键的地方
                // 2. this 指向调用对象,即 wrap 中的 generator
                return this._invoke("next", arg);
            }
        });
        genFun.prototype = generator;
        return genFun;
    };

经过分析,最后执行的是 function invoke 。invoke 函数其中的一个 value 结果来自 Babel 编译出的 helloWorldGenerator$ 函数调用后的返回结果。

  • 关于生成器内部的构成
    • 参考:js 忍者秘籍第6章
    • 关于生成器的执行完成与否,以及生成器每次 next 返回结果的控制是通过 wrap 函数 context 变量中的 done,next,prev 等一些值维护的。根据 context 的值在 invoke 函数中进行 executing, compeleted, yield 各种状态的转换。
    • 为什么 next 能多次执行?主要原因是形成了一个闭包,保存了某些变量不被销毁。在 chrome 浏览器中调试这段代码,可以查看闭包的存在。(不过我倒是没看懂为什么闭包存下来的变量是那两个,之后有时间再瞅瞅)

image

@Amybiubiu Amybiubiu added the JS label Feb 10, 2021
@Amybiubiu
Copy link
Owner Author

  • 顺带在这补一下 Babel 将 Async, Await 生成了什么?
    通过 Babel 的 try it out 查看编译后的代码,对 await 个函数会经过 regenerator.wrap 函数包装,该函数的作用是使各函数具有 next 方法,以便之后的迭代执行,之后再通过 _asyncToGernerator 函数迭代执行各部分,执行过程是把 next 函数加入到 promise.then 中,最后形成多个 promise.then 嵌套的形式,也就是用 promise 串行异步代码的方式。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant