JavaScript Promise 速查表 - Promise 异步编程与 async/await 大全
这张速查给在浏览器(fetch)或 Node 里写异步代码的 JS 开发者。内容从创建与消费 Promise 讲起,再讲 all、race、allSettled、any 这几个初学者最容易搞混的静态组合方法,接着是链式调用及其上的 async/await 语法糖。后面的章节覆盖真实场景:并发发起大量请求而不打爆连接、用超时等待最快结果、对不稳定请求做重试,以及用 AbortController 中止正在进行的 fetch。读完你应能按顺序编排异步步骤、给并行任务加上并发热度上限,并为易失败的调用补上超时与重试。
典型使用场景
面向需要把回调改成 async/await、或要真正控制并发与取消的 Node.js 与浏览器开发者。Promise 的难点从不是语法,而是三个选择:多个异步任务该用 all / allSettled / race / any 中的哪一个;串行与并发的差别在性能上是数量级的;以及请求超时、取消、重试怎么写才不漏。本表覆盖 Promise 的创建与状态流转、四个组合子的语义差异、async/await 与 try/catch 的对应关系、并发池与超时控制,以及 AbortController 中止与 deferred 模式。
基础用法 5
new Promise((resolve, reject) => { resolve(value) })new Promise((resolve, reject) => { reject(new Error("fail")) })Promise.resolve(value)Promise.reject(reason)new Promise((resolve) => setTimeout(resolve, 1000, "ok"))消费 Promise 5
promise.then((result) => {})promise.catch((error) => {})promise.finally(() => {})promise.then(onFulfilled, onRejected)promise.then((v) => v + 1)静态方法 5
Promise.all([p1, p2])Promise.race([p1, p2])Promise.allSettled([p1, p2])Promise.any([p1, p2])Promise.allSettled 结果项为 { status, value } 或 { status, reason }链式调用 4
fetch(url).then((r) => r.json()).then((data) => {})promise.then(() => nextPromise())promise.then((v) => v + 1).then((v) => console.log(v))promise.catch((err) => fallback()).then((v) => {})async/await 5
async function fn() { return value }const result = await promisetry { await promise } catch (err) {}await Promise.all([p1, p2])const [a, b] = await Promise.all([fa(), fb()])进阶模式 6
for (const item of items) { await process(item) }await Promise.all(items.map((i) => fetch(i)))async function pool(tasks, limit) {}Promise.race([task(), timeout(5000)])async function retry(fn, times = 3) {}async function sleep(ms) { return new Promise((r) => setTimeout(r, ms)) }取消与工具 6
const ac = new AbortController(); ac.abort()fetch(url, { signal: ac.signal })ac.signal.addEventListener("abort", () => {})AbortSignal.timeout(3000)queueMicrotask(() => {})const d = {}; d.promise = new Promise((r, j) => { d.resolve = r; d.reject = j })参数矩阵
| 参数 | 作用 | 示例 |
|---|---|---|
Promise.all([p1, p2]) | 全部成功才成功,任一失败立即整体失败 | const [a, b] = await Promise.all([fa(), fb()]) |
Promise.allSettled([p1, p2]) | 等全部完成,逐个返回状态,永不整体失败 | Promise.allSettled 结果项为 { status, value } 或 { status, reason } |
Promise.race([p1, p2]) | 取第一个完成的(不论成败),用于超时控制 | Promise.race([task(), timeout(5000)]) |
Promise.any([p1, p2]) | 取第一个成功的,全部失败才失败 | Promise.any([p1, p2]) |
promise.finally(cb) | 成功失败都执行,适合关 loading、释放资源 | promise.finally(() => {}) |
AbortController / ac.abort() | 主动中止关联的异步操作 | const ac = new AbortController(); ac.abort() |
AbortSignal.timeout(3000) | 3 秒后自动发出中止信号,无需手动计时 | AbortSignal.timeout(3000) |
items.map((i) => fetch(i)) | 并发发起全部任务,而不是逐个 await | await Promise.all(items.map((i) => fetch(i))) |
deferred 模式 | 把 resolve / reject 暴露到 Promise 外部手动控制 | const d = {}; d.promise = new Promise((r, j) => { d.resolve = r; d.reject = j }) |
queueMicrotask(cb) | 把回调放进微任务队列,早于定时器执行 | queueMicrotask(() => {}) |
易错点与避坑指南
现象for 循环里 await,10 个请求跑了十几秒,接口明明各自只要 1 秒。
原因循环内 await 是串行的,必须等上一个结束才发下一个,总耗时等于各请求之和。
处置改成并发:await Promise.all(items.map((i) => fetch(i)));需要限流时再套一层并发池 async function pool(tasks, limit) {}。
现象catch 写在了链尾却没接住中间抛出的错误,页面报 Uncaught (in promise)。
原因错误发生在没有返回 Promise 的同步代码里,或 catch 被挂在了另一个未返回的分支上。
处置确认链上每一步都 return 了 Promise;混用回调时改用 try { await promise } catch (err) {} 包裹整段。
现象Promise.race 超时后请求并没有真的停下,服务端日志还在持续打印。
原因race 只是让外层先结束,底层任务仍在跑,没有被取消。
处置配合 AbortController:const ac = new AbortController(); ac.abort() 并在 fetch(url, { signal: ac.signal }) 上生效;仅需超时的场景可用 AbortSignal.timeout(3000)。
现象loading 状态在异常路径下没关掉,界面永远转圈。
原因清理写在 then 的成功回调里,失败分支没有对应处理。
处置用 promise.finally(() => {}) 统一收尾,成功与失败都会执行。
现象批量任务中一个失败导致其余成功结果全部丢失。
原因用了 Promise.all,首个 reject 就让整体失败,其它结果无处可取。
处置改用 Promise.allSettled([p1, p2]),逐项读取 { status, value } 或 { status, reason }。
现象组件卸载后异步回调仍在更新状态,报警告或出现幽灵数据。
原因异步流程没有绑定取消信号,组件已经销毁而 Promise 链仍在继续。
处置在 effect 中创建 AbortController,卸载时调用 ac.abort(),并用 ac.signal.addEventListener("abort", () => {}) 做收尾。
排障路径
1给一个 Promise 加超时上限
Promise.race([task(), timeout(5000)])race 让外层 5 秒后先返回;真正取消底层任务还需要 AbortController 配合。
2把回调式 API 包成可 await 的函数
async function sleep(ms) { return new Promise((r) => setTimeout(r, ms)) }这一句就能把任意回调 API 变成 await 友好的工具函数,是迁移期最好用的胶水。
3失败自动重试
async function retry(fn, times = 3) {}重试要配合超时与退避,否则一次网络抖动会把后端打满。
4在错误被吞掉之前记录它
promise.catch((err) => fallback()).then((v) => {})catch 返回 fallback 值后链继续往下走,适合让页面降级而不是白屏。
5需要外部手动控制完成时机时用 deferred
const d = {}; d.promise = new Promise((r, j) => { d.resolve = r; d.reject = j })适用于把回调式接口接入 async 流程,或在测试中精确控制 resolve 时机。
提示
- Promise 三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败),状态不可逆。
- then 返回新 Promise,链式调用中返回值会自动包装并传给下一个 then。
- catch 等同于 then(null, onRejected),能捕获链上任意位置的拒绝。
- Promise.all 适合全部必须成功;allSettled 适合需要全部结果;race 取最快;any 取首个成功。
- async/await 是 Promise 的语法糖,用 try/catch 处理错误更直观。
由 巧匠 维护
公开更新于 2026年7月21日,内容持续校对官方文档。