async / await 原理

1. 明确概念

async 函数就是 Generator 函数的语法糖。把 Generator 函数的 * 换成 async,把 yield 替换成 await。

2. async 对 Generator 的改进

  • 内置执行器:不需要使用 next() 手动执行。
  • await 后面的值更广:可以是 Promise 对象或原始类型的值;yield 命令后面只能是 Thunk 函数或 Promise 对象。
  • 返回值是 Promise:返回非 Promise 时,async 函数会把它包装成 Promise 返回(Promise.resolve(value))。
    • 若 async 中没有 return,返回的是 Promise<undefined>。
    • 若有 return:return 的值会作为返回 Promise 的 then 回调值。

3. 作用

异步编程的终极解决方案。

4. 通俗理解

async/await 是异步编程回调函数写法的替代方法——使代码以同步方式的写法完成异步操作。

5. 原理示例

// 用 Generator + 自动执行器模拟 async/await
function asyncToGenerator(genFunc) {
  return function () {
    const gen = genFunc.apply(this, arguments);
    return new Promise((resolve, reject) => {
      function step(key, arg) {
        let res;
        try { res = gen[key](arg); }
        catch (e) { return reject(e); }
        const { value, done } = res;
        if (done) return resolve(value);
        Promise.resolve(value).then(
          (v) => step('next', v),
          (e) => step('throw', e),
        );
      }
      step('next');
    });
  };
}

const fetchUser = asyncToGenerator(function* () {
  const data = yield new Promise(r => setTimeout(() => r('Tom'), 100));
  console.log(data); // 'Tom'
});
fetchUser();

补充:

  • await 后跟非 Promise 值,会被 Promise.resolve 包装。
  • await 后面的 Promise 若 reject,会"穿透" try/catch 抛到外层,不会继续往下执行。
  • 多个 await 会串行执行,若彼此无依赖应改用 Promise.all 以提高并发性能。
  • 循环中 await 时,若希望并发请求所有数据后再进入下一步,可以先 Promise.all(arr.map(asyncFn))。

来源整理自:vue3js.cn 面试官系列