关于 Babel 编译后的 Generator 状态机结构解析
一、为什么 Babel 要编译成 Generator?
老版本浏览器不支持 async/await
,所以 Babel 会:
async function fn() { // 定义一个异步函数 fn,使用 async 关键字表明函数内部可以使用 awaitconst res = await getData(); // 调用异步函数 getData(),等待其执行完成后,将返回值赋给 res 变量return res + 1; // 返回 res 加 1 的结果;这个 return 的结果会自动被封装成一个 Promise 对象
}
编译成:
var fn = _asyncToGenerator( // 调用 _asyncToGenerator 把一个 Generator 函数转成支持 Promise 的 async 函数regeneratorRuntime.mark(function* _callee() { // 使用 regeneratorRuntime.mark 包装一个 Generator 函数,生成器函数名为 _calleevar res; // 定义变量 res,用于存放 getData 的返回结果return regeneratorRuntime.wrap(function (_context) { // 包装内部状态控制逻辑(状态机),_context 管理执行流程while (1) switch (_context.prev = _context.next) { // 状态机循环,通过 switch 和 _context 控制流程跳转case 0: // case 0:初始状态_context.next = 2; // 跳转到状态 2,并执行 yield 表达式(实际是 await)return getData(); // yield getData(),此处暂停,等待 Promise 返回(相当于 await getData())case 2: // case 2:getData 执行完成,结果通过 _context.sent 获取res = _context.sent; // 把 yield 表达式(即 getData())的返回值赋给 res_context.abrupt("return", res + 1); // 立即中断函数并返回 res + 1,相当于 return res + 1}}, _callee); // 绑定到 _callee 生成器函数上})
);
二、结构总览(模块分解)
编译后核心结构包括这几个部分:
名称 | 解释 |
---|---|
_asyncToGenerator() | 把 Generator 包装成异步 Promise |
regeneratorRuntime.mark(fn) | 把函数标记为 Generator |
regeneratorRuntime.wrap(fn) | 把函数包裹成状态机控制结构 |
while (1) switch(...) | 状态切换结构 |
_context.next | 跳转到下一个状态 |
_context.sent | 获取上一个 yield/await 的返回值 |
_context.abrupt() | 立即返回或退出当前状态 |
三、状态机结构详解
源码:
async function test() {const a = await getData();const b = await parseData(a);return b;
}
编译后:
var test = _asyncToGenerator( // _asyncToGenerator:将生成器函数转换为返回 Promise 的函数(兼容 async)regeneratorRuntime.mark(function* () { // regeneratorRuntime.mark:将匿名 Generator 函数进行注册和标记var a, b; // 定义两个变量 a 和 b,用来存储异步操作的结果return regeneratorRuntime.wrap(function (_context) { // 包装状态机,_context 是内部上下文控制器while (1) switch (_context.prev = _context.next) { // 无限循环 + 状态切换机制(状态机)case 0: // 初始状态 case 0_context.next = 2; // 设置下一步跳转到 case 2,同时 yield 一个异步操作return getData(); // yield getData(),相当于 await getData(),暂停执行等待结果case 2: // 等 getData() Promise resolve 后,跳转到这里a = _context.sent; // 获取 getData() 的结果,赋值给变量 a_context.next = 5; // 设置下一步为 case 5return parseData(a); // yield parseData(a),等价于 await parseData(a)case 5: // 等 parseData() 执行完后跳到这里b = _context.sent; // 获取 parseData(a) 的结果,赋值给变量 b_context.abrupt("return", b); // 立即中断并返回 b,等价于 return b}});})
);
四、状态转移图解
状态是通过 switch (_context.prev = _context.next)
控制的:
状态 0 → _context.next = 2return getData(); // await getData()状态 2 → a = _context.sent_context.next = 5return parseData(a); // await parseData(a)状态 5 → b = _context.sentreturn b; // return 结果
每个 case 就是一个逻辑步骤。每次 return 表示 await,每次 sent 是拿返回值。
五、关键语句详解
语句 | 作用 | 等价于原始 JS |
---|---|---|
_context.next = N; return foo(); | 执行 foo,并跳转到第 N 步 | await foo(); |
res = _context.sent; | 拿到上一步 yield 的结果 | const res = await xxx; |
_context.abrupt("return", xxx); | 提前退出函数,返回值为 xxx | return xxx; |
六、执行流程详解(配图思维)
async function fn() {const res = await getData(); // Step 1await saveData(res); // Step 2return "done"; // Step 3
}
变成:
switch (_context.prev = _context.next) { // 状态机入口,切换执行状态case 0: // 第 0 步,初始状态_context.next = 2; // 指定下一步为 case 2return getData(); // step 1:执行 getData(),暂停函数等待 Promise 结果(相当于 await getData())case 2: // 第 2 步,getData() 执行完,继续执行res = _context.sent; // 获取 getData() 的结果,赋值给 res(相当于 const res = await getData())_context.next = 5; // 指定下一步为 case 5return saveData(res); // step 2:执行 saveData(res),暂停函数等待 Promise 结果(相当于 await saveData(res))case 5: // 第 5 步,saveData(res) 执行完,继续执行_context.abrupt("return", "done"); // step 3:返回 "done",终止 Generator(相当于 return "done")
}
每一步是状态跳转 + 执行 + 下一步
七、如何手动还原 async/await?
只需 3 步:
-
找
_context.next = N; return xxx
→ 就是await xxx
-
找
xxx = _context.sent;
→ 表示上一步结果赋值 -
找
_context.abrupt("return", ...)
→ 函数的 return
八、技巧总结
实战分析点 | 如何应对 |
---|---|
_context.sent | 找到结果变量,理解 await 获取值 |
_context.next | 理解跳转执行顺序 |
abrupt("return") | 确定退出函数并返回值 |
多层嵌套 Generator | 拆解并逐步打印每层函数 |