🚀把 async/await 拆成 4 块乐高!面试官当场鼓掌👏

面试官:"说说 async/await 的原理。"

你:"就是语法糖。"

面试官:"展开讲讲。"

你:"......"

别急,今天我们把 async/await 拆成 4 块乐高: Promise → Generator → 自执行器 → async/await。拼完就再也不会卡壳。


一、前菜:Promise 的"链式地狱"

js 复制代码
function fetchUser(id) {
  return fetch(`/user/${id}`).then(r => r.json());
}

fetchUser(1)
  .then(user => fetch(`/order/${user.lastOrder}`))
  .then(order => fetch(`/detail/${order.id}`))
  .then(console.log)
  .catch(console.error);

缺点:横向金字塔、错误处理分散、变量作用域混乱。

需求:像同步那样写异步 ------ async/await 登场!


二、第一块积木:Promise

  • async 函数 永远返回一个 Promise。
  • await 只能等 Promise 结果。
js 复制代码
async function demo() {
  return 123;   // 自动包一层 Promise.resolve(123)
}
demo().then(console.log); // 123

三、第二块积木:Generator 暂停/恢复

Generator 可以"停住函数、记住上下文、外部注入值",是 async/await 的底层发动机。

js 复制代码
function* gen() {
  const a = yield 1; // 停!把 1 交出去,等外部把结果塞回 a
  const b = yield 2 + a;
  return b;
}

const g = gen();
g.next();      // {value: 1, done: false}
g.next(10);    // 把 10 塞回 a,继续到 yield 12,{value: 12}
g.next(100);   // 把 100 塞回 b,{value: 100, done: true}

四、第三块积木:自执行器(co 库 20 行实现)

把 Generator 的 .next() 写成自动循环,就成了 co:

js 复制代码
function co(gen) {
  return new Promise((resolve, reject) => {
    const g = gen();
    function step(val) {
      const { value, done } = g.next(val);
      if (done) return resolve(value);
      Promise.resolve(value).then(step, reject);
    }
    step();
  });
}

使用:

js 复制代码
co(function* () {
  const user = yield fetchUser(1);
  const order = yield fetch(`/order/${user.lastOrder}`).then(r => r.json());
  return order;
}).then(console.log);

这就是 Generator + 自执行器 的"伪 async/await"。


五、第四块积木:官方 async/await ------ 语法糖合体

Babel 转译后,你的 async 函数 ≈ 被 co 包装后的 Generator:

源码

js 复制代码
async function getDetail(id) {
  const user = await fetchUser(id);
  const order = await fetch(`/order/${user.lastOrder}`).then(r => r.json());
  return order;
}

转译后(简化)

js 复制代码
function getDetail(id) {
  return co(function* () {
    const user = yield fetchUser(id);
    const order = yield fetch(`/order/${user.lastOrder}`).then(r => r.json());
    return order;
  });
}

六、错误处理三板斧

场景 写法
try/catch 捕获 try { await xxx } catch(e) {}
并发错误 Promise.allSettled([...])
超时兜底 await Promise.race([fetch(), sleep(5000)])

七、并发与串行对比代码

js 复制代码
// 串行:2 s + 2 s = 4 s
const a = await delay(2000);
const b = await delay(2000);

// 并发:max(2 s, 2 s) = 2 s
const [a, b] = await Promise.all([delay(2000), delay(2000)]);

八、手写 async/await 面试题

题目:用 Generator 实现 sleep 函数 + async/await 效果。

js 复制代码
function sleep(ms) {
  return new Promise(r => setTimeout(r, ms));
}

function* genSleep() {
  console.time('sleep');
  yield sleep(1000);
  console.timeEnd('sleep'); // ≈ 1000 ms
}

co(genSleep);

九、思维导图总结

javascript 复制代码
async/await
├─ 语法糖
├─ 基于 Promise
├─ 内部使用 Generator + 自执行器
└─ Babel 转译 ≈ co 库

🏁 结语

把 async/await 拆成 4 块乐高后,你会发现:

  • 它并不神秘,只是 Promise + Generator + 自执行器 的优雅封装。
  • 写代码时:串行/并发、错误、超时,套路固定。
  • 面试回答时:先画模型,再给代码,稳拿 Offer!

下次面试官再问 async/await 原理,你可以自信地说:

"先生,我会拼乐高。"

相关推荐
Fuyo_111934 分钟前
C++ 运行时多态与虚函数机制全面剖析【面试重点】
开发语言·c++·面试
孟健1 小时前
Claude Code 同需求写两版对比:装不装 frontend-design 差多远?
前端·ai编程·claude
IT规划师1 小时前
一文讲透五种发布机制:滚动、蓝绿、金丝雀、红黑与 AB
后端·面试
OpsEye2 小时前
大模型 Token 计量不准,是什么原因,怎么解决??
javascript·ai编程
梦de天空一米16883 小时前
【一个重复id让游戏卡在62%,聊聊配置表检查】
前端
ryn483983 小时前
01 一个功能模块,怎么分层才不乱
前端·ai编程·claude
Amos_Web3 小时前
通过 Rspack 源码学 Rust(五):读懂 Result 与问号运算符的错误返回
前端
杨航 AI3 小时前
JEV的用法
java·前端·数据库
Amos_Web3 小时前
Rspack 源码解析(三十五):rspack_error 如何用 miette 渲染带源码定位的错误链
前端
去伪存真3 小时前
高敏资产场景下的大模型协同设计架构
前端·agent