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

这也是前端必考。

相关推荐
爱上好庆祝34 分钟前
学习js的第五天
前端·css·学习·html·css3·js
AI人工智能+电脑小能手38 分钟前
【大白话说Java面试题】【Java基础篇】第26题:Java的抽象类和接口有哪些区别
java·开发语言·面试
C澒1 小时前
IntelliPro 产研协作平台:基于 AI Agent 的低代码智能化配置方案设计与实现
前端·低代码·ai编程
一袋米扛几楼981 小时前
【Git】规范化协作:详解 GitHub 工作流中的 Issue、Branch 与 Pull Request 最佳实践
前端·git·github·issue
网络点点滴1 小时前
前端与后端的区别与联系
前端
EnCi Zheng2 小时前
M5-markconv自定义CSS样式指南 [特殊字符]
前端·css·python
kyriewen2 小时前
你的网页慢,用户不说直接走——前端性能监控教你“读心术”
前端·性能优化·监控
广州华水科技2 小时前
北斗GNSS变形监测在大坝安全监测中的应用与优势分析
前端
前端老石人2 小时前
前端开发中的 URL 完全指南
开发语言·前端·javascript·css·html
CAE虚拟与现实2 小时前
五一假期闲来无事,来个前段、后端的说明吧
前端·后端·vtk·three.js·前后端