标准答案

  1. 关系:async/await 是基于 Promise 的语法糖。async 函数一定返回 Promise;return 普通值会变成 fulfilled Promise,函数内部抛错会变成 rejected Promise。
  2. await 行为:await 后面可以跟 Promise 或普通值。遇到 Promise 时,async 函数会暂停后续执行,等 Promise settled 后再继续;await 后面的续执行会进入微任务。
  3. 异常处理:可以用 try/catch 包住 await,也可以让 async 函数返回 rejected Promise 交给外层 catch。无论哪种方式,都要保证调用方能处理失败。
  4. 并发写法:多个互不依赖的异步任务不要连续 await,否则会串行执行。应该先创建多个 Promise,再用 Promise.all、allSettled 等组合等待;只有后一个任务依赖前一个结果时才顺序 await。

题目解析

async/await 的价值是改善异步代码可读性,但它没有改变 Promise 的本质。async 函数返回 Promise,await 后续执行进入微任务,这些底层规则决定了错误处理和执行顺序。

最重要的工程点是并发和串行的区别。多个互不依赖的请求如果逐个 await,代码看起来顺,但性能会变差。判断标准是:后一个任务是否依赖前一个结果。

异常处理也不能只背 try/catch。async 函数内部抛错会变成 rejected Promise,如果调用方不 await、不 catch,仍然会形成未处理的 rejection。实际项目里要明确错误在哪一层收口。

如果多个请求都要展示部分结果,Promise.all 可能不是最佳选择,因为一个失败会导致整体失败。这个问题通常会自然引出 allSettled、请求取消和竞态处理。

代码示例

下面同时给出串行写法和并发写法,方便对比执行方式。

JavaScript
async function loadPageData() {
  // 如果两个请求互不依赖,不要这样写:
  // const user = await fetchUser()
  // const posts = await fetchPosts()

  const userTask = fetchUser()
  const postsTask = fetchPosts()

  try {
    const [user, posts] = await Promise.all([userTask, postsTask])
    return { user, posts }
  } catch (error) {
    // 统一处理请求失败
    throw error
  }
}

常见误区

  • 误以为 async 函数返回的是普通值。
  • 在循环里无脑 await,导致本可以并发的请求变成串行。
  • 只写 try/catch,不考虑 Promise.all 中一个失败会整体失败。

作者信息