🚀把 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 原理,你可以自信地说:

"先生,我会拼乐高。"

相关推荐
Wcowin19 分钟前
MkDocs文档日期插件【推荐】
前端·mkdocs
小徐不徐说33 分钟前
C++ 模板与 STL 基础入门:从泛型编程到实战工具集
开发语言·数据结构·c++·qt·面试
xw51 小时前
免费的个人网站托管-Cloudflare
服务器·前端
网安Ruler1 小时前
Web开发-PHP应用&Cookie脆弱&Session固定&Token唯一&身份验证&数据库通讯
前端·数据库·网络安全·php·渗透·红队
!win !1 小时前
免费的个人网站托管-Cloudflare
服务器·前端·开发工具
饺子不放糖1 小时前
基于BroadcastChannel的前端多标签页同步方案:让用户体验更一致
前端
饺子不放糖2 小时前
前端性能优化实战:从页面加载到交互响应的全链路优化
前端
Jackson__2 小时前
使用 ICE PKG 开发并发布支持多场景引用的 NPM 包
前端
饺子不放糖2 小时前
前端错误监控与异常处理:构建健壮的Web应用
前端
cos2 小时前
FE Bits 前端周周谈 Vol.1|Hello World、TanStack DB 首个 Beta 版发布
前端·javascript·css