JavaScript Promise 速查表 - Promise 异步编程与 async/await 大全

这张速查给在浏览器(fetch)或 Node 里写异步代码的 JS 开发者。内容从创建与消费 Promise 讲起,再讲 all、race、allSettled、any 这几个初学者最容易搞混的静态组合方法,接着是链式调用及其上的 async/await 语法糖。后面的章节覆盖真实场景:并发发起大量请求而不打爆连接、用超时等待最快结果、对不稳定请求做重试,以及用 AbortController 中止正在进行的 fetch。读完你应能按顺序编排异步步骤、给并行任务加上并发热度上限,并为易失败的调用补上超时与重试。

编程语言·共 36 条命令·最后更新 2026-07-21
promise异步asyncjavascript

典型使用场景

面向需要把回调改成 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,常用于把同步值转异步
Promise.reject(reason)
直接得到已失败(已拒绝)的 Promise,测试或快速失败用
new Promise((resolve) => setTimeout(resolve, 1000, "ok"))
延时 1 秒后带着 "ok" 成功,模拟耗时操作或加等待

消费 Promise 5

promise.then((result) => {})
成功时执行的回调,参数是 Promise 的完成值
promise.catch((error) => {})
失败时执行的回调,负责处理拒绝原因
promise.finally(() => {})
成功或失败都会执行,适合关 loading、清理资源
promise.then(onFulfilled, onRejected)
一次指定成功与失败两个回调,等价于 then + catch
promise.then((v) => v + 1)
回调返回的值会被自动包装成新 Promise 继续传递

静态方法 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 串联异步
promise.then((v) => v + 1).then((v) => console.log(v))
返回值向下游传递
promise.catch((err) => fallback()).then((v) => {})
错误恢复后继续链式

async/await 5

async function fn() { return value }
async 函数自动返回 Promise
const result = await promise
等待 Promise 完成
try { await promise } catch (err) {}
用 try/catch 捕获拒绝
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)])
超时控制(5 秒)
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 })
fetch 关联中止信号
ac.signal.addEventListener("abort", () => {})
监听中止事件
AbortSignal.timeout(3000)
3 秒后自动中止
queueMicrotask(() => {})
把回调加入微任务队列
const d = {}; d.promise = new Promise((r, j) => { d.resolve = r; d.reject = j })
deferred 模式(外部控制 resolve)

参数矩阵

参数作用示例
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))并发发起全部任务,而不是逐个 awaitawait 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. 1给一个 Promise 加超时上限

    Promise.race([task(), timeout(5000)])

    race 让外层 5 秒后先返回;真正取消底层任务还需要 AbortController 配合。

  2. 2把回调式 API 包成可 await 的函数

    async function sleep(ms) { return new Promise((r) => setTimeout(r, ms)) }

    这一句就能把任意回调 API 变成 await 友好的工具函数,是迁移期最好用的胶水。

  3. 3失败自动重试

    async function retry(fn, times = 3) {}

    重试要配合超时与退避,否则一次网络抖动会把后端打满。

  4. 4在错误被吞掉之前记录它

    promise.catch((err) => fallback()).then((v) => {})

    catch 返回 fallback 值后链继续往下走,适合让页面降级而不是白屏。

  5. 5需要外部手动控制完成时机时用 deferred

    const d = {}; d.promise = new Promise((r, j) => { d.resolve = r; d.reject = j })

    适用于把回调式接口接入 async 流程,或在测试中精确控制 resolve 时机。

命令示例

创建、成功与失败的最小形态

new Promise((resolve, reject) => { resolve(value) }) new Promise((resolve, reject) => { reject(new Error("fail")) }) Promise.resolve(value) Promise.reject(reason)

Promise.resolve 把任意同步值包装成已完成的 Promise,Promise.reject 直接得到一个已失败的 Promise。

串行与并发的耗时差

for (const item of items) { await process(item) } await Promise.all(items.map((i) => fetch(i)))

前者逐个等待、总耗时相加;后者由 map 同步创建全部 Promise,请求同时在跑,耗时接近最慢的一个。

用组合子区分全部成功与逐项状态

await Promise.all([p1, p2]) const settled = await Promise.allSettled([p1, p2]) await Promise.race([p1, p2]) await Promise.any([p1, p2])

all 要全部成功,race 取第一个完成(不论成败),any 取第一个成功;allSettled 的每项是 { status, value } 或 { status, reason }。

超时控制与真正的中止

const ac = new AbortController() ac.signal.addEventListener("abort", () => {}) fetch(url, { signal: ac.signal }) Promise.race([task(), timeout(5000)]) AbortSignal.timeout(3000) ac.abort()

race 只让外层先返回超时结果;要真正停掉请求必须调用 ac.abort(),AbortSignal.timeout(3000) 则在 3 秒后自动发出中止信号。

常见坑与注意事项

  • for 循环里逐个 await 会让耗时线性叠加,批量任务请改成 await Promise.all(items.map(...)) 或用并发池限流。
  • Promise.race 拿到超时结果不代表底层请求已停止,不调 ac.abort() 的话请求仍会跑完并在服务端留下日志。
  • promise.finally 才是清理 loading 与资源的正确位置,只写在成功分支里会让异常路径永远转圈。
  • catch 只能捕获它所挂载位置之后的拒绝,挂在链尾的 catch 接不住中间回调抛出的错误。
  • Promise.all 中任一失败即整体失败,其余并发任务的结果不会被返回;需要保留每项结果就改用 Promise.allSettled。

提示

  • Promise 三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败),状态不可逆。
  • then 返回新 Promise,链式调用中返回值会自动包装并传给下一个 then。
  • catch 等同于 then(null, onRejected),能捕获链上任意位置的拒绝。
  • Promise.all 适合全部必须成功;allSettled 适合需要全部结果;race 取最快;any 取首个成功。
  • async/await 是 Promise 的语法糖,用 try/catch 处理错误更直观。

常见问题

Promise.all 和 Promise.allSettled 该怎么选?

判据只有一个:结果能不能丢。后续步骤依赖全部数据才能继续,就用 Promise.all,它在任一失败时立即整体失败,错误传播路径最短;如果每个任务的结果都要拿到、单个失败不该拖垮整批,就用 Promise.allSettled,它等全部完成后逐个返回 { status, value } 或 { status, reason },你自己决定怎么处理失败项。批量写操作里通常还要注意 all 会在第一个失败时就抛错,此时其余任务已经在并发跑但结果不会被你看到。

for 循环里逐个 await 为什么会这么慢?

因为 await 让出事件循环,下一次迭代要等前一个完成才开始,10 个各耗 1 秒的请求就是约 10 秒。并发写法是把任务先全部发起、最后一起等:await Promise.all(items.map((i) => fetch(i)))。map 在前面同步地创建出所有 Promise,网络请求因此同时在跑,耗时接近最慢的那一个。需要限制并发压力时用 async function pool(tasks, limit),让窗口内最多 limit 个任务在飞。

AbortController 真的能停掉请求吗?

能停掉 fetch 这类实现了 signal 的操作:把 ac.signal 传给 fetch(url, { signal: ac.signal }),调用 ac.abort() 后请求会被中断。但要注意 Promise.race([task(), timeout(5000)]) 只是让外层先拿到超时的结果,内部那个请求默认仍在跑;真正要停必须持有 controller 并调用 ac.abort()。需要等 3 秒自动中止时可以用 AbortSignal.timeout(3000),要感知中止时刻就 ac.signal.addEventListener("abort", ...) 注册监听。

async 函数里 throw 之后,调用方怎么才能拿到错误?

async 函数返回的仍然是 Promise,它内部 throw 等价于返回一个被拒绝的 Promise。调用方要么用 try { await promise } catch (err) {} 捕获,要么在链上挂 promise.catch((error) => {})。容易漏的是错误发生在链的中间而 catch 挂在末尾之外——catch 只能捕获它所挂载的那一段之后的错误,所以异步处理最好直接用 async/await 加 try/catch,位置一目了然。

loading 状态为什么在异常路径下关不掉?

因为关 loading 的回调多半只写在成功分支里。promise.finally(() => {}) 在成功和失败时都会执行,把清理动作(关 loading、释放连接、隐藏骨架屏)统一放进 finally,异常路径就不会漏。想同时确认每个任务的状态再统一收尾,就用 Promise.allSettled([p1, p2]) 的结果逐项判断,别在中间某处提前 return。

官方参考来源

下方为命令对应的官方权威文档,供你核对最新用法与深入查阅。

由 巧匠 维护

公开更新于 2026年7月21日,内容持续校对官方文档。

联系我们

命令或描述有误?提交反馈、商务合作或产品建议都可发送邮件给我们。

联系我们