← 返回题目列表

async/await 的原理是什么?和 Promise 有什么关系?

高频 中等 第 7 / 28 题 更新于 2026/07/27
ES6asyncawaitPromise

简化版

async/await 是 Promise 的语法糖。async 函数总是返回 Promise,await 会等待 Promise 结果,并让后续代码以微任务形式继续执行。它让异步代码看起来像同步流程,但不会阻塞主线程。

详细版

示例:

async function load() {
  try {
    const user = await fetchUser();
    return user.name;
  } catch (err) {
    console.error(err);
  }
}

等价于基于 Promise 的链式流程。await 后如果是普通值,会被包装成 resolved Promise;如果 Promise rejected,会抛出异常,可用 try/catch 捕获。

注意:多个互不依赖的异步任务不要顺序 await,应该用 Promise.all 并发。

完整版教学

一、async 函数返回 Promise

无论你在 async 函数里返回什么,外面拿到的都是 Promise:

async function f() {
  return 1;
}
f().then(console.log); // 1

如果函数内部抛错,返回的是 rejected Promise。

二、await 不阻塞主线程

await 会暂停当前 async 函数的后续执行,把后续逻辑放到微任务里等待 Promise 完成。它不会阻塞整个 JS 主线程,其他同步代码仍然可以继续执行。

async function f() {
  console.log(1);
  await Promise.resolve();
  console.log(2);
}
f();
console.log(3);
// 1 3 2

三、错误处理更接近同步代码

Promise 链用 .catch,async/await 可以用 try/catch

try {
  await request();
} catch (e) {
  showError(e);
}

这让多步异步流程更好读。

四、避免不必要的串行

错误写法:

const a = await fetchA();
const b = await fetchB();

如果二者无依赖,会浪费时间。更好:

const [a, b] = await Promise.all([fetchA(), fetchB()]);

五、面试追问与工程落地

async/await 常见追问是“await 后面的代码是同步还是异步”。await 前同步执行,await 后会在 Promise settle 后继续,通常进入微任务队列。这就是为什么 await Promise.resolve() 后面的日志会晚于当前栈里的同步日志。

还会问“循环里能不能 await”。如果任务必须按顺序执行,比如后一项依赖前一项结果,可以用 for...of + await;如果任务互相独立,应该先创建 Promise,再 Promise.all 并发,否则会把并发请求写成串行请求。

工程中还要处理并发失败策略。Promise.all 任何一个失败就整体失败;如果希望拿到所有结果,无论成功失败,可以用 Promise.allSettled。这在批量上传、批量请求看板数据时更合适。

六、用时间线判断执行顺序与并发

await expression 会先求值表达式并按 Promise 语义吸收结果,然后暂停当前 async 函数;函数之外的同步代码仍继续执行。即便等待的是数字 1,await 后面的续体也不会在当前同步调用栈中立即执行,而是作为异步任务继续。回答输出顺序题时,应按“同步代码 → 已入队的微任务”逐行推演。

console.log('A')
async function run() {
  console.log('B')
  await 1
  console.log('C')
}
run()
Promise.resolve().then(() => console.log('D'))
console.log('E')
// A、B、E、C、D

并发题还要区分“创建 Promise”和“等待 Promise”。假设两个互不依赖的请求各耗时约 100 ms,连续两次 await request() 总耗时约 200 ms;先创建两个 Promise 再 await Promise.all,理想总耗时约 100 ms。这里的数字用于解释关键路径,不代表网络请求一定精确按时结束。

写法启动关系失败表现适用条件
连续 await前一个完成后再启动在当前点抛出后者依赖前者
Promise.all任务先并发启动任一拒绝即整体拒绝全部都必须成功
Promise.allSettled任务先并发启动收集每项状态允许部分失败

“await 不阻塞线程”只说明 JavaScript 执行栈会让出;当前 async 函数本身仍暂停,错误的串行写法照样会拉长业务耗时。

七、常见误区与追问

  • 误区:async 函数只有写了 await 才返回 Promise。 async 函数每次调用都会立即返回一个 Promise,普通返回值会成为该 Promise 的兑现结果。
  • 误区:await 会阻塞浏览器主线程。 它只暂停当前 async 函数,调用栈释放后其他同步代码和后续任务仍可运行。
  • 误区:循环中出现 await 就一定正确。 for...of 中逐次 await 会串行执行;是否正确取决于任务间有没有依赖和限流要求。
  • 追问:await 一个普通值会同步继续吗? 不会,后续代码仍异步恢复,因此它可以改变日志输出顺序。
  • 追问:async 函数 return 一个 Promise 会返回同一个对象吗? 返回的新 Promise 会跟随该 Promise 的状态,但对象身份通常不同。
  • 追问:Promise.all 失败后其他请求会自动取消吗? 不会,它只提前拒绝聚合结果;取消底层任务需要 AbortController 等额外机制。
  • 追问:try/catch 能捕获没有 await 的 Promise 拒绝吗? 不能捕获已经直接返回或遗漏等待的异步拒绝;需要 awaitreturn Promise 或在 Promise 链上处理。

八、加强记忆

async/await 不是新异步模型,而是 Promise 的舒适写法。async 包 Promise,await 等结果,错误用 try/catch,独立任务用 Promise.all 并发。

遇到执行顺序题先标出每个暂停点,遇到性能题再判断 Promise 是何时创建的,就能避免把语法顺序误当作任务启动顺序。