async/await 是 Promise 的语法封装,不能替代 Promise;async 函数自动返回 Promise,await 只能在 async 函数内使用,会暂停当前函数但不阻塞主线程,错误需用 try/catch 捕获。
async/await 不是新语法糖,而是 Promise 的语法封装;它不能替代 Promise,但能让异步流程像同步代码一样可读、可调试。
声明一个 async 函数时,JavaScript 引擎会自动把它包装成返回 Promise 的函数——哪怕你 return 42,实际返回的是 Promise.resolve(42)。
throw new Error()),等价于返回 Promise.reject()
return await somePromise() 包裹顶层调用,这多一层不必要的等待;直接 return somePromise() 更高效async 函数内部仍需用 try/catch 捕获 await 的异常,而不是靠外层 .catch()
async function fetchUser() {
try {
const res = await fetch('/api/user');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return await res.json();
} catch (err) {
console.error('Failed to fetch user:', err.message);
throw err; // 保持 reject 链路清晰
}
}
这是常见报错源头:await is only valid in async functions。它不是独立语句,而是 async 函数的专属操作符。
addEventListener)、模块顶层直接写 await
top-level await,但仅限 ES 模块(type="module"),且会影响模块初始化顺序--experimental-top-level-await(v14.8+ 默认启用),但生产环境慎用,尤其涉及依赖顺序时很多人误以为 await 会让整个 JS 线程卡住。其实它只是暂停当前 async 函数的执行,把控制权交还给事件循环,其他任务(如点击事件、定时器)照常运行。
await 是串行的:多个请求依次发起,总耗时 ≈ 各请求耗时之和Promise.all([p1, p2, p3]) 包裹后 await,总耗时 ≈ 最慢那个请求await Promise.allSettled([...]) 更稳妥,即使某个请求失败也不中断其余请求
async function loadAll() {
// ❌ 串行:3 秒 + 2 秒 + 1 秒 = ~6 秒
const a = await fetch('/a');
const b = await fetch('/b');
const c = await fetch('/c');
// ✅ 并发:~3 秒(最长那个)
const [a, b, c] = await Promise.all([
fetch('/a'),
fetch('/b'),
fetch('/c')
]);
}
最常被忽视的点:没包 try/catch,又没接 .catch(),导致未捕获异常(unhandledrejection)。
await 后面的 Promise 被 reject,若没 try/catch,会触发全局 unhandledrejection 事件process.on('unhandledRejection'))await someAsyncFn().catch(...) —— 这样 await 等到的是 undefined 或 catch 返回值,语义混乱真正该做的是:始终用 try/catch 包住每个需要容错的 await 表达式,或明确用 .catch() 处理并返回默认值。