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

这也是前端必考。

相关推荐
写写闲篇儿1 小时前
微软面试之白板做题
面试·职场和发展
程序员爱钓鱼1 小时前
Node.js 编程实战:文件读写操作
前端·后端·node.js
PineappleCoder1 小时前
工程化必备!SVG 雪碧图的最佳实践:ID 引用 + 缓存友好,无需手动算坐标
前端·性能优化
JIngJaneIL2 小时前
基于springboot + vue古城景区管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端
敲敲了个代码2 小时前
隐式类型转换:哈基米 == 猫 ? true :false
开发语言·前端·javascript·学习·面试·web
澄江静如练_2 小时前
列表渲染(v-for)
前端·javascript·vue.js
liang_jy3 小时前
Android LaunchMode
android·面试
JustHappy3 小时前
「chrome extensions🛠️」我写了一个超级简单的浏览器插件Vue开发模板
前端·javascript·github
Loo国昌3 小时前
Vue 3 前端工程化:架构、核心原理与生产实践
前端·vue.js·架构