【babel】babel如何编译async语法

2023/02/01 14:21:32

示例

const getData = () =>
  new Promise((resolve) => setTimeout(() => resolve("data"), 1000));

async function test() {
  const data = await getData();
  console.log("data: ", data);
  const data2 = await getData();
  console.log("data2: ", data2);
  return "success";
}

// 这样的一个函数 应该再1秒后打印data 再过一秒打印data2 最后打印success
test().then((res) => console.log(res));

babel 编译结果

原始编译代码

对于上面的示例,babel 编译结果如下(只节选关键内容):

function test() {
  return _test.apply(this, arguments);
} // 这样的一个函数 应该再1秒后打印data 再过一秒打印data2 最后打印success
function _test() {
  _test = _asyncToGenerator(
    /*#__PURE__*/ _regeneratorRuntime().mark(function _callee() {
      var data, data2;
      return _regeneratorRuntime().wrap(function _callee$(_context) {
        while (1)
          switch ((_context.prev = _context.next)) {
            case 0:
              _context.next = 2;
              return getData();
            case 2:
              data = _context.sent;
              console.log("data: ", data);
              _context.next = 6;
              return getData();
            case 6:
              data2 = _context.sent;
              console.log("data2: ", data2);
              return _context.abrupt("return", "success");
            case 9:
            case "end":
              return _context.stop();
          }
      }, _callee);
    })
  );
  return _test.apply(this, arguments);
}

整理编译代码

将上面编译后的代码调整成容易理解的样子:

function _test() {
  fn1 = function _callee() {
    var data, data2;
    fn2 = function _callee$(_context) {
      while (1)
        switch ((_context.prev = _context.next)) {
          case 0:
            _context.next = 2;
            return getData();
          case 2:
            data = _context.sent;
            console.log("data: ", data);
            _context.next = 6;
            return getData();
          case 6:
            data2 = _context.sent;
            console.log("data2: ", data2);
            return _context.abrupt("return", "success");
          case 9:
          case "end":
            return _context.stop();
        }
    };
    return _regeneratorRuntime().wrap(fn2, _callee);
  };
  fn = /*#__PURE__*/ _regeneratorRuntime().mark(fn1);
  _test = _asyncToGenerator(fn);
  return _test.apply(this, arguments);
}

提取关键代码

function _test() {
  fn1 = function _callee {
    fn2 = function _callee$(_context) {
      // ...
    }
    // ...
    return _regeneratorRuntime().wrap(fn2, _callee);
  };
  fn = /*#__PURE__*/ _regeneratorRuntime().mark(fn1);
  _test = _asyncToGenerator(fn);
  return _test.apply(this, arguments);
}

要搞这段代码的逻辑需要了解这些问题:

  • _asyncToGenerator() 方法的作用。
  • _regeneratorRuntime() 方法的作用,以及它返回的 .wrap().mark() 方法的作用。
  • _callee()_callee$() 方法的作用。

babel 对 async/await 的实现

在 babel 编译后的代码中可以找到其定义:

function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
  try {
    var info = gen[key](arg);
    var value = info.value;
  } catch (error) {
    reject(error);
    return;
  }
  if (info.done) {
    resolve(value);
  } else {
    Promise.resolve(value).then(_next, _throw);
  }
}
function _asyncToGenerator(fn) {
  return function () {
    var self = this,
      args = arguments;
    return new Promise(function (resolve, reject) {
      var gen = fn.apply(self, args);
      function _next(value) {
        asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
      }
      function _throw(err) {
        asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
      }
      _next(undefined);
    });
  };
}

_asyncToGenerator(fn) 接收一个函数(看起来像是一个 Generator 函数),返回一个 Promise,具体操作由 asyncGeneratorStep() 方法执行,并由该方法决定这个 Promise 何时 resolve 或 reject。

asyncGeneratorStep() 方法分析:

/**
 * @param gen _asyncToGenerator(fn) 中根据 Generator 函数(fn)生成的迭代器
 * @param resolve _asyncToGenerator(fn) 返回值 Promise 的 resolve 方法
 * @param reject _asyncToGenerator(fn) 返回值 Promise 的 reject 方法
 * @param _next 接收一个参数,作为下一次调用 asyncGeneratorStep() 方法的 arg
 * @param _throw 接收一个参数,作为下一次调用 asyncGeneratorStep() 方法的 arg
 * @param key 'next' | 'throw' 用来标记本次需要调用 gen 的 .next() 还是 .throw()
 * @param arg 迭代器上次迭代的返回值
 */
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
  try {
    // 调用 gen 的 .next() 或 .throw() 方法,并将上次迭代的返回值传递下去
    var info = gen[key](arg);
    var value = info.value;
  } catch (error) {
    reject(error);
    return;
  }
  if (info.done) {
    resolve(value);
  } else {
    Promise.resolve(value).then(_next, _throw);
  }
}

可见 _asyncToGenerator()asyncGeneratorStep() 其实就是对 asyncawait 的实现:_asyncToGenerator() 接收类似 Generator 的函数,并且一步步执行 yield 语句,最终返回一个 promise。