关于 Babel 编译后的 Generator 状态机结构解析

一、为什么 Babel 要编译成 Generator?

老版本浏览器不支持 async/await,所以 Babel 会:

async function fn() {                      // 定义一个异步函数 fn,使用 async 关键字表明函数内部可以使用 await
  const 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 函数,生成器函数名为 _callee
    var 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 5
          return 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 = 2
    return getData();          // await getData()

状态 2 → a = _context.sent
        _context.next = 5
        return parseData(a);   // await parseData(a)

状态 5 → b = _context.sent
        return 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 1
  await saveData(res);         // Step 2
  return "done";               // Step 3
}

变成:

switch (_context.prev = _context.next) {       // 状态机入口,切换执行状态
  case 0:                                       // 第 0 步,初始状态
    _context.next = 2;                          // 指定下一步为 case 2
    return getData();                           // step 1:执行 getData(),暂停函数等待 Promise 结果(相当于 await getData())

  case 2:                                       // 第 2 步,getData() 执行完,继续执行
    res = _context.sent;                        // 获取 getData() 的结果,赋值给 res(相当于 const res = await getData())
    _context.next = 5;                          // 指定下一步为 case 5
    return 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 拆解并逐步打印每层函数

你可能感兴趣的:(前端,javascript,开发语言)