Skip to content

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

New issue

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

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

Already on GitHub? # to your account

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 串行异步代码的方式。

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

No branches or pull requests

1 participant