第 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)

这也是前端必考。

相关推荐
黄敬峰15 分钟前
一文搞懂 AI 聊天应用的 Memory 模块:Milvus 向量数据库实战
面试
leoZ2311 小时前
第 6 篇:SchemaForm 渲染器核心实现
前端·javascript·vue.js·人工智能·神经网络·机器学习·自然语言处理
CoderYanger1 小时前
前端基础——JavaScript(基础语法)(下篇)
java·开发语言·前端·javascript·程序人生·面试·职场和发展
还是大剑师兰特1 小时前
vue项目浏览器版本判别,低于IE11跳转到新页面
前端·javascript·vue.js
计算机魔术师1 小时前
奥尔特曼致歉 GPT-6 Astra 发布混乱,现已面向所有 Plus / Pro 等用户推出
前端
落樱弥城1 小时前
RHI 设计考量:不同 API 差异分析
服务器·前端·性能优化
热心网友俣先生2 小时前
2026国赛C题:五年命题规律与预测
java·c语言·前端·数学建模
BestHeaker3 小时前
跨企业接口对接:IQDS / CPK 数据解析与协作避坑指南(五)
java·服务器·前端
青花锁4 小时前
OpenAI Function Calling 完全指南:让大模型安全调用你的业务系统
前端·安全·functioncalling
俊昭喜喜里4 小时前
C#中的func<>
java·前端·c#