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 源码