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

"先生,我会拼乐高。"

相关推荐
hh随便起个名11 分钟前
力扣二叉树的三种遍历
javascript·数据结构·算法·leetcode
写写闲篇儿35 分钟前
微软面试之白板做题
面试·职场和发展
我是小路路呀1 小时前
element级联选择器:已选中一个二级节点,随后又点击了一个一级节点(仅浏览,未确认选择),此时下拉框失去焦点并关闭
javascript·vue.js·elementui
程序员爱钓鱼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_jy2 小时前
Android LaunchMode
android·面试