async/await 原理与实践

1. 核心原理

async/await 是 ES8 引入的语法糖,其本质是 Generator 函数 + 自动执行器 的封装。

  • async: 将函数标记为异步,确保函数返回一个 Promise
  • await: 暂停代码执行,等待右侧 Promise 解决(resolve),并将解包后的结果作为表达式的值。

它让异步代码看起来像同步代码,解决了 Promise 链式调用(.then().then())带来的语义不连贯问题。

2. 执行顺序分析 (非常重要)

面试中常考代码输出顺序。

console.log('script start');

async function async1() {
  console.log('async1 start');
  await async2();
  console.log('async1 end');
}

async function async2() {
  console.log('async2');
}

async1();

console.log('script end');

解析:

  1. script start (宏任务)
  2. async1 开始执行 -> async1 start
  3. 遇到 await async2() -> 执行 async2 (同步) -> 输出 async2
  4. 关键点await 后面的代码(console.log('async1 end'))被放入微任务队列,跳出 async1 函数。
  5. script end (宏任务继续)
  6. 宏任务执行完毕,检查微任务队列 -> 执行 async1 剩余代码 -> 输出 async1 end

3. 错误处理

这也是相比 Promise 链的一大优势,可以使用标准的 try/catch

async function fetchUser() {
  try {
    const user = await getUser();
    const posts = await getPosts(user.id);
  } catch (err) {
    // 捕获 getUser 或 getPosts 中的 reject
    console.error(err);
  }
}

4. 并行与串行 (性能优化)

陷阱:不要在循环中无意地串行使用 await

// ❌ 串行 (慢)
async function serial() {
  const a = await fetchA(); // 等待 A 完成
  const b = await fetchB(); // 再请求 B
}

// ✅ 并行 (快)
async function parallel() {
  const pA = fetchA();
  const pB = fetchB();
  const a = await pA;
  const b = await pB;
}

// ✅ 使用 Promise.all (推荐)
const [a, b] = await Promise.all([fetchA(), fetchB()]);

5. 面试加分项

  • 顶层 await: ES2022 允许在模块顶层使用 await,无需包裹在 async 函数中。
  • async 函数返回值: 总是返回 Promise。如果不报错且返回非 Promise 值,会通过 Promise.resolve 包装。