[4-3] 异步编程与事件循环 · 终极异步解决方案 (Generator & Async/Await)

45 阅读3分钟

所属板块:4. 异步编程与事件循环

记录日期:2026-03-xx
更新:遇到 async/await 输出题或并发控制题时补充

1. async/await 的本质(Generator + Promise 语法糖)

async/await 是 Generator + Promise 的语法糖,是 JS 异步编程的终极方案。

前置基础:Generator

Generator 是 ES6 引入的"可暂停/恢复执行"的函数,用 function* 定义,yield 暂停。它本身不能直接处理异步,但提供了 async/await 所需的暂停/恢复能力:

function* gen() {
  yield 1;
  yield 2;
  return 3;
}

const g = gen();
console.log(g.next());   // { value: 1, done: false }
console.log(g.next());   // { value: 2, done: false }
console.log(g.next());   // { value: 3, done: true }

关键:Generator 遇到 yield 会暂停,等外部调用 next() 才恢复。如果 yield 的是 Promise,执行器可以 .then() 里自动调 next(),就实现了异步流程控制:

// 极简执行器,演示 Generator → async/await 的桥梁
function run(genFn) {
  const gen = genFn();
  function step(val) {
    const { value, done } = gen.next(val);
    if (!done) Promise.resolve(value).then(step);
  }
  step();
}

run(function* () {
  const a = yield Promise.resolve(1);  // 暂停,等 Promise resolve 后恢复
  console.log(a);                       // 1
});

async/await 就是把这个过程自动化了——引擎内置了执行器,你不再需要手动写 run

async/await 核心特性

  • async 函数的返回值一定是一个 Promise
  • await 后面如果不是 Promise,会被自动 Promise.resolve() 包装
  • await 下方的代码会被包装成 .then() 回调,推入微任务队列(结合 [4-1] 事件循环)
async function test() {
  console.log("1");
  await Promise.resolve("await");
  console.log("2");
}

test();
console.log("3");

输出:1 → 3 → 2(await 转为微任务)

输出:1 → 3 → 2

错误处理

推荐用 try...catch 包裹 await 调用:

async function fetchData() {
  try {
    const res = await fetch('/api');
    const data = await res.json();
    return data;
  } catch (err) {
    console.error("请求失败", err);
  }
}

2. await 在 Event Loop 中的真实表现(极其重要)

  • await 右侧表达式同步执行
  • await 下方的所有代码被包装成微任务
  • 如果 await 的是已 resolved 的 Promise,仍会推入微任务队列(不会立即执行)

经典对比:

async function asyncFunc() {
  console.log("async start");
  await Promise.resolve();
  console.log("async end");
}

asyncFunc();
console.log("global");

输出:async start → global → async end

输出:async start → global → async end

3. 并发优化(最容易踩坑)

fetch() 调用的本质是"启动即返回"——调用时立即发起网络请求并返回一个 Promise,不等响应回来。所以 urls.map(url => fetch(url)) 会同时启动 N 个请求,然后 Promise.all 等它们全部完成。串行 await 则是等一个回来再发下一个,白白浪费了并行时间:

// ❌ 串行执行:每个 fetch 等响应回来才发下一个
for (const url of urls) {
  const data = await fetch(url);
}

// ✅ 并发启动:同时发出所有请求,再统一等
const promises = urls.map(url => fetch(url));
const results = await Promise.all(promises);

4. 小结 & 第四板块完结

  • async/await 是 Generator + Promise 的语法糖,是 JS 异步编程的终极写法
  • await 本质是微任务调度(必须结合 [4-1] 事件循环理解)
  • [4-1] 事件循环 + [4-2] Promise + 本文 async/await,共同构成了 JS 异步编程的完整体系

第四板块(异步编程与事件循环)到此全部结束。
这是 JS 单线程下最核心的"动态调度机制",掌握后刷输出题和写并发代码会非常顺手。

返回总目录:戳这里