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

"先生,我会拼乐高。"

相关推荐
yzzzzzzzzzzzzzzzzz5 小时前
JavaScript 操作 DOM
开发语言·javascript·ecmascript
奋斗的小羊羊5 小时前
HTML5关键知识点之多种视频编码工具的使用方法
前端·音视频·html5
前端呆猿5 小时前
深入解析HTML5中的object-fit属性
前端·css·html5
再学一点就睡5 小时前
实现大文件上传全流程详解(补偿版本)
前端·javascript·面试
你的人类朋友6 小时前
【Node&Vue】什么是ECMAScript?
前端·javascript·后端
路灯下的光7 小时前
用scss设计一下系统主题有什么方案吗
前端·css·scss
l_tian_tian_7 小时前
SpringClound——网关、服务保护和分布式事务
linux·服务器·前端
一只小风华~7 小时前
CSS @media 媒体查询
前端·css·媒体
shix .8 小时前
最近 | 黄淮教务 | 小工具合集
前端·javascript
John_ToDebug8 小时前
Chrome 内置扩展 vs WebUI:浏览器内核开发中的选择与实践
前端·c++·chrome