第 28 题:手写 async/await(Generator 自动执行器原理)

好的,继续 第 34 题:手写 async/await(Generator 自动执行器原理)

这是高级工程师面试的重点,常用于考察你对 async/await 的底层理解。


一、为什么要手写 async/await?

面试官喜欢让你证明:

  • 你理解 async/await 是 Promise + Generator 的语法糖
  • 你知道 await 是如何"暂停"函数的
  • 你能写一个自动执行器(类似 TJ 的 co 库)

所以我们需要实现:

✔ 自动迭代 Generator

✔ 遇到 Promise 自动等待

✔ yield 结果继续向下执行

这就模拟了 async/await。


二、目标:把 Generator 变成 async 函数

假设一个 Generator:

javascript 复制代码
function* gen() {
  const a = yield Promise.resolve(1);
  const b = yield Promise.resolve(a + 1);
  return b + 1;
}

我们希望让它这样运行:

scss 复制代码
run(gen).then(console.log); // 输出 3

三、手写一个 async/await 的核心代码(简化版)

下面这段代码是面试够用的核心:

vbnet 复制代码
function run(genFn) {
  return new Promise((resolve, reject) => {
    const gen = genFn();

    function step(nextF, arg) {
      let next;
      try {
        next = nextF.call(gen, arg);
      } catch (e) {
        return reject(e);
      }

      if (next.done) {
        return resolve(next.value);
      }

      Promise.resolve(next.value).then(
        v => step(gen.next, v),
        e => step(gen.throw, e)
      );
    }

    step(gen.next);
  });
}

四、它如何实现 async/await?(逐行解释)

1. 启动 Generator

ini 复制代码
const gen = genFn();

2. 定义 step,用于迭代 Generator

javascript 复制代码
function step(nextF, arg) { ... }

3. 调用 next / throw:

ini 复制代码
next = nextF.call(gen, arg);

4. 如果 done === true → 表示 return 出来了 → resolve

lua 复制代码
if (next.done) return resolve(next.value);

5. 如果 yield 得到一个 Promise

javascript 复制代码
Promise.resolve(next.value)

6. Promise 成功 → 调用 next 继续执行

ini 复制代码
v => step(gen.next, v)

7. Promise 失败 → 调用 throw 抛回 Generator

ini 复制代码
e => step(gen.throw, e)

五、这段代码等价于 async/await 的机制

比如 async 写法:

csharp 复制代码
async function test() {
  const a = await foo();
  return a + 1;
}

等价于 Generator 写法:

javascript 复制代码
function* test() {
  const a = yield foo();
  return a + 1;
}

等价于调用自动执行器:

scss 复制代码
run(test);

这就是 async/await 的本质!


六、面试官常问的补充点

1. 为什么要用 Promise.resolve 包装?

因为 yield 后面可能不是 Promise,也要兼容。


2. async/await 为什么会更慢?

  • 语法糖 → 额外包装 Promise
  • 微任务更多
  • 不能并行,需要串行等待

3. 怎么让多个 await 并行?

错误方式(串行):

arduino 复制代码
await a();
await b();

正确方式(并行):

ini 复制代码
await Promise.all([a(), b()]);

七、20 秒面试背诵版

async/await 本质是 Generator 的语法糖。

await = yield + 自动执行器。

我们可以通过写一个 co 风格的执行器来模拟 async/await:

即不断调用 next(),遇到 Promise 等待完成后继续执行,直到 done=true 为止。


如果你愿意,我可以继续 第 35 题:模块化原理(CommonJS vs ESModule)

这也是前端必考。

相关推荐
木叶子---13 小时前
前端打包出错
前端·人工智能·tensorflow
JAVA面经实录91714 小时前
前端系统化学习计划表(含完整知识思维导图)
前端·学习
牛油果子哥q14 小时前
STL set与map底层精讲,红黑树适配原理、有序去重特性、迭代器遍历、API实战与面试核心考点全解
开发语言·数据结构·c++·面试
本末倒置18314 小时前
开发了一个所见所得的md编辑器,致敬Typora大佬
前端
kyriewen14 小时前
TypeScript 高级类型:我用 infer 写了一个类型安全的 EventBus,终于搞懂了泛型约束
前端·javascript·typescript
UXbot14 小时前
原型设计工具如何帮助新人快速进入产品行业?
前端·低代码·ui·交互·团队开发·原型模式·web app
yoothey15 小时前
MySQL事务机制解析 - 面试高分知识点
数据库·mysql·面试
JAVA面经实录91715 小时前
操作系统(面试全覆盖)
java·计算机网络·面试
黄敬峰15 小时前
从 DFS 遍历到抖音推荐算法:前端工程师的硬核复习笔记
前端