讲解
async/await 是 Promise 的语法糖,让异步代码读起来像同步代码。async 修饰的函数永远返回 Promise:函数体里 return 什么,调用方就拿到包装了这个值的 Promise。await 只能用在 async 函数(或模块顶层)里,它「暂停」当前函数的执行,等后面的 Promise 落定:成功则把结果作为整个 await 表达式的值,失败则把 reject 的原因当作异常抛出——所以 try/catch 能自然地接住异步错误,这是它比 then 链优雅的核心原因。
await 的暂停只影响当前 async 函数,不阻塞其他代码,这是理解执行顺序的关键。另一个高频考点是并发:连着写 await a(); await b() 是串行(b 等 a 完成),如果两个请求互不依赖,应该同时发出再一起等——const [ra, rb] = await Promise.all([a(), b()]),总耗时取两者最大值而非之和。性能敏感的接口编排里,这个差别就是响应时间翻倍和不变的差别。
错误处理的最佳实践:在 async 函数内用 try/catch 包住 await;调用 async 函数的一方同样要面对 Promise(要么 await 要么 .catch);循环里 await 数组用 Promise.all + map(并发)或 for...of(严格串行),不要在 forEach 里 await——forEach 不会等回调里的 Promise,这是经典翻车点。
示例
async/await 的等待是异步的,沙盒里先看同步可观察的事实:
// async 函数调用后同步返回一个 Promise
async function getNumber() {
return 42;
}
const result = getNumber();
console.log('async 返回值类型:', result instanceof Promise);
// await 前的同步部分立即执行
async function demo() {
console.log('函数开始:这行同步执行');
await Promise.resolve(); // 从这里起,后续代码进入微任务
console.log('await 之后:异步继续');
}
demo();
console.log('调用之后:这行先于 await 之后的输出');
// async 函数抛错 = 返回 rejected Promise
async function fail() {
throw new Error('出错了');
}
fail().catch((e) => console.log('外层 catch 到:', e.message));
console.log('reject 不会中断同步代码');
真实的异步编排示意(浏览器控制台或 Node 中运行):
const delay = (ms, v) => new Promise((r) => setTimeout(() => r(v), ms));
async function loadPage() {
try {
const user = await delay(300, '用户信息'); // 串行:必须等上一步
const orders = await delay(300, user + '的订单');
return orders;
} catch (err) {
console.error('加载失败:', err.message);
return null;
} finally {
console.log('收尾:关闭 loading');
}
}
// 并发:互不依赖的请求同时发
async function loadDashboard() {
const [stats, notices, todos] = await Promise.all([fetchStats(), fetchNotices(), fetchTodos()]);
return { stats, notices, todos };
}
// 数组处理:并发用 map + Promise.all,串行用 for...of
async function processAll(ids) {
const results = await Promise.all(ids.map((id) => fetchItem(id))); // 并发
// for (const id of ids) { await fetchItem(id); } // 严格串行
return results;
}
常见坑
- 在 forEach/map 回调里直接 await:forEach 不等 Promise,循环瞬间发完全部、后续逻辑在结果回来前就执行。要么 for...of 串行,要么 map 出 Promise 数组后 Promise.all。
- 把互不依赖的 await 写成串行:await a(); await b() 白白多花一倍时间。先并发发起再一起 await。
- 顶层直接 await:await 只能在 async 函数或 ES 模块顶层;普通脚本里写 await 报语法错误,需要包一层 async 函数。
- async 函数出错没人接:调用 async 函数却不 await 也不 .catch,reject 变成未处理拒绝。谁来调用,谁负责处理失败。
小结
async 函数返回 Promise,await 暂停当前函数并取出结果或抛出错误;串行连写、并发用 Promise.all;try/catch 统一处理异步错误。下一章揭开 await 背后的调度机制:事件循环。