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
对象记录当前执行位置(如prev
、next
等状态码)。
简化源码实现
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);
});
mark(gen)
:将gen
标记为生成器函数,绑定原型方法。wrap(gen$, gen)
:gen$
是状态机函数,通过switch/case
管理yield
位置。context
保存prev/next
状态码(如0→2→4
)。
- 迭代过程:
- 首次
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 源码。