Skip to content

Babel转译

regeneratorRuntime.mark()regeneratorRuntime.wrap() 是 Babel 转译 Generator 函数和 async/await 时使用的核心运行时函数,主要用于管理生成器的状态机和执行上下文。以下是它们的源码解析和协作机制:


一、regeneratorRuntime 的背景与作用

regeneratorRuntime 是 Facebook 开发的 Regenerator 库的运行时环境,用于实现 Generator 的暂停/恢复能力。当 Babel 转译 ES6+ 的 Generator 或 async 函数时,会将其转换为依赖 regeneratorRuntime 的 ES5 代码。若未正确导入该运行时,会抛出 regeneratorRuntime is not defined 错误。


二、mark() 函数的原理与源码解析

作用

  • 标记生成器函数:将普通函数包装为生成器函数,为其注入状态机逻辑。
  • 创建迭代器原型:生成器调用时返回的迭代器对象继承自此原型。

简化源码实现

javascript
runtime.mark = function (genFun) {
    genFun.__proto__ = GeneratorFunctionPrototype; // 继承生成器原型
    genFun.prototype = Object.create(Gp); // 绑定原型方法 (next/throw/return)
    return genFun;
};
  • GeneratorFunctionPrototype:包含生成器函数的共享方法(如 Symbol.iterator)。
  • Gp 对象:包含迭代器方法(next()throw()return()),是生成器实例的原型。

三、wrap() 函数的原理与源码解析

作用

  • 创建状态机上下文:将生成器函数包装成一个可暂停/恢复的执行单元。
  • 管理执行状态:通过 context 对象记录当前执行位置(如 prevnext 等状态码)。

简化源码实现

javascript
runtime.wrap = function (innerFn, outerFn, self) {
    var context = {
        state: 0, // 当前状态码
        tryEntries: [], // try-catch 栈
        prev: 0, // 上一个 yield 的位置
        next: 0, // 下一个 yield 的位置
        done: false // 生成器是否执行完毕
    };

    return {
        next: function (arg) {
            context.sent = arg; // 传入外部值
            return invoke(innerFn, context); // 执行状态机
        },
        throw: function (exception) {
            /* 错误处理 */
        },
        return: function (value) {
            /* 提前终止 */
        }
    };
};
  • invoke() 函数:根据 context.state 跳转到对应代码位置(通过 switch/case 实现)。

四、执行流程分析(以生成器为例)

假设原始生成器函数:

javascript
function* gen() {
    yield 'a';
    yield 'b';
}

Babel 转译后的伪代码:

javascript
var _gen = /*#__PURE__*/ regeneratorRuntime.mark(function gen() {
    return regeneratorRuntime.wrap(function gen$(_context) {
        while (1) {
            switch ((_context.prev = _context.next)) {
                case 0:
                    _context.next = 2;
                    return 'a';
                case 2:
                    _context.next = 4;
                    return 'b';
                case 4:
                case 'end':
                    return _context.stop();
            }
        }
    }, gen);
});
  1. mark(gen):将 gen 标记为生成器函数,绑定原型方法。
  2. wrap(gen$, gen)
    • gen$ 是状态机函数,通过 switch/case 管理 yield 位置。
    • context 保存 prev/next 状态码(如 0→2→4)。
  3. 迭代过程
    • 首次 next()state=0,返回 'a',状态暂停在 next=2
    • 再次 next():跳转到 case 2,返回 'b',状态更新为 next=4

五、在异步应用中的协作机制

Generator 与 Promise 结合实现异步控制流:

javascript
function* fetchData() {
    const data = yield fetch('/api');
    console.log(data);
}

const it = fetchData();
const promise = it.next().value; // 获取 fetch 返回的 Promise

promise.then((response) => {
    it.next(response); // 将结果传回生成器
});
  • wrap() 的上下文管理:在 yield 处暂停,通过 it.next(response) 恢复执行并传值给 data

六、与 async/await 的关系

async 函数本质上是 Generator 的语法糖:

javascript
// async/await 版本
async function fetchData() {
    const data = await fetch('/api');
    console.log(data);
}

// Babel 转译后等价于:
fetchData = regeneratorRuntime.mark(function _fetchData() {
    return regeneratorRuntime.wrap(function _fetchData$(_ctx) {
        // 状态机逻辑类似
    }, _fetchData);
});
  • 自动执行:async 函数隐式调用 next(),无需手动驱动迭代器。

总结:关键设计思想

函数核心作用实现机制
mark()将普通函数标记为生成器原型链继承 + 绑定迭代器方法
wrap()创建状态机上下文,管理暂停/恢复switch/case + 上下文对象
协作效果将生成器转换为可中断/恢复的状态机通过闭包保存上下文,实现无阻塞异步

此设计解决了 JavaScript 单线程下异步任务的“暂停-恢复”问题,为 async/await 奠定了底层基础。实际代码可参考 Regenerator 源码