在前端开发中,我们经常需要同时处理多个异步任务,比如请求多个接口、加载多个资源等。为了更高效地管理这些并发操作,JavaScript 提供了
Promise的几个静态方法:Promise.all、Promise.race、Promise.any和Promise.allSettled。它们各有特点:有的要求全部成功才算成功,有的只看谁跑得快,有的只要有一个成功就行,还有的会等所有任务结束并返回每个的结果。掌握这些方法,能让我们更灵活、简洁地处理复杂的异步场景。
本文将用简单的方式介绍这几种方法的用途和区别,帮助你快速理解和使用。
1. Promise.all
定义:
Promise.all 接收一个 Promise 可迭代对象(如数组),并返回一个新的 Promise。只有当所有输入的 Promise 都成功(fulfilled)时,新 Promise 才会成功,结果是一个按顺序排列的值数组;如果任意一个 Promise 失败(rejected),则立即失败并返回第一个错误。
特点:
- 全成功才成功:所有
Promise必须完成且无错误。 - 立即失败:只要有一个
Promise失败,整个结果失败。 - 顺序保持:结果数组的顺序与输入顺序一致,即使某些
Promise执行较慢。
适用场景:
- 并发处理多个独立任务,且需要所有任务都成功后才能继续(如同时获取多个接口数据)。
- 需要严格依赖所有任务结果的场景(如渲染页面前需加载所有资源)。
基础代码:
// p1: 一个立即成功(fulfilled)的 Promise,值为 'p1 resolve',用于模拟瞬间完成的异步操作。
const p1 = Promise.resolve('p1 resolve')
// p2: 一个延迟 2 秒后成功(fulfilled)的 Promise,值为 'p2 延迟2s',用于模拟耗时约 2 秒的异步请求。
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p2 延迟2s')
}, 2000)
})
// p3: 一个延迟 3 秒后成功(fulfilled)的 Promise,值为 'p3 延迟3s',用于模拟耗时约 3 秒的异步请求。
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p3 延迟3s')
}, 3000)
})
// p4: 一个立即失败(rejected)的 Promise,原因为 'p4 reject',用于模拟同步阶段就出错的场景。
const p4 = Promise.reject('p4 reject')
// p5: 一个延迟 1 秒后失败(rejected)的 Promise,原因为 'p5 延迟 1s',用于模拟异步操作中出现错误(如网络超时)。
const p5 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('p5 延迟 1s')
}, 1000)
})
示例:
// 案例1:所有 Promise 成功
Promise.all([p1, p2, p3])
.then((results) => {console.log(results);}) // ["p1 resolve", "p2 延迟2s", "p3 延迟3s"]
.catch((error) => {console.error(error);});
// 结果顺序与输入顺序一致,即使 p3 最慢
// 案例2:一个 Promise 立即失败(p4)
Promise.all([p1, p4, p2])
.then((results) => {console.log(results);}) // 不会触发
.catch((error) => {console.error(error); });// "p4 reject"
// 任意一个失败,立即返回第一个错误
// 案例3:一个 Promise 延迟失败(p5)
Promise.all([p1, p5, p2])
.then((results) => {console.log(results);}) // 不会触发
.catch((error) => {console.error(error);}); // "p5 延迟 1s"
// p5 是第一个失败的 Promise,all 立即返回错误
// 案例4:不同执行时间的 Promise,验证顺序保持
Promise.all([p2, p3, p5])
.then((results) => {console.log(results); })// ["p2 延迟2s", "p3 延迟3s", "p5 延迟 1s"]
.catch((error) => {console.error(error);});
// "p5 延迟 1s"(因 p5 失败导致 all 立即失败)
// 结果顺序与输入顺序一致,即使 p5 最快完成
效果描述:
- 案例1:所有
Promise成功,结果数组的顺序与输入顺序一致(p1、p2、p3),即使p3最慢完成。
验证了 顺序保持 的特性。 - 案例2:
p4是第一个失败的Promise,导致整个Promise.all立即失败,返回p4的错误信息。
验证了 立即失败 的特性。 - 案例3:
p5是第一个失败的Promise,all立即返回其错误,无需等待其他Promise完成。
验证了 立即失败 的特性。 - 案例4:尽管
p5最快完成,但结果数组仍与输入顺序一致。
2. Promise.race
定义:
Promise.race 接收一个 Promise 可迭代对象,并返回一个新的 Promise。新 Promise 的状态由第一个完成(无论成功或失败)的 Promise 决定。
特点:
- 谁快听谁的:无论成功或失败,第一个完成的
Promise决定最终结果。 - 竞速逻辑:适用于超时控制或优先响应的场景。
适用场景:
- 需要快速响应的场景(如网络请求的超时处理)。
- 竞争式任务(如多个异步操作中选择最快的结果)。 示例:
// 案例1:第一个是成功的 Promise(p1)
Promise.race([p1, p2, p3])
.then((result) => {console.log(result);}) // "p1 resolve"(p1 最快完成)
.catch((error) => {console.error(error);}); // 不会触发,因为 p1 成功
// 案例2:第一个是失败的 Promise(p4)
Promise.race([p4, p5])
.then((result) => {console.log(result);}) // 不会触发
.catch((error) => {console.error(error);}); // "p4 reject"(p4 最快完成且失败)
// 案例3:混合成功和失败的 Promise(p5 延迟失败)
Promise.race([p5, p2])
.then((result) => {console.log(result);}) // 不会触发
.catch((error) => {console.error(error);}); // "p5 延迟 1s"(p5 比 p2 更快完成且失败)
效果描述:
- 案例1:
p1是最快的,且成功,因此结果直接返回p1的值。 - 案例2:
p4是最快的,但失败,因此整个race直接返回p4的错误。 - 案例3:
p5比p2更快完成,但失败,因此结果由p5决定。
验证了race的核心特性:第一个完成的Promise(无论成功或失败)决定了最终结果。
3. Promise.any
定义:
Promise.any 接收一个 Promise 可迭代对象,并返回一个新的 Promise。新 Promise 在任意一个输入的 Promise 成功时立即成功,结果为该成功值;只有当所有 Promise 都失败时才会失败,返回一个包含所有错误的 AggregateError。
特点:
- 首个成功即成功:忽略其他失败的
Promise。 - 全失败才失败:所有
Promise失败时,返回聚合错误。
适用场景:
- 并行尝试多个操作,只需一个成功即可(如多服务器请求,任选一个可用结果)。
- 容错性要求高的场景(如备用方案选择)。
示例:
// 案例1:至少一个成功(p1)
Promise.any([p1, p4, p5])
.then((result) => {console.log(result);}) // "p1 resolve"(p1 成功)
.catch((error) => {console.error(error);}); // 不会触发
// 案例2:全部失败(p4、p5)
Promise.any([p4, p5])
.then((result) => {console.log(result);}) // 不会触发
.catch((error) => {console.error(error);}); // AggregateError(包含所有错误)
// 案例3:混合成功和失败的 Promise(p1 成功)
Promise.any([p4, p5, p1])
.then((result) => {console.log(result);}) // "p1 resolve"(p1 成功)
.catch((error) => {console.error(error);}); // 不会触发
效果描述:
- 案例1:
p1成功,因此直接返回其值,忽略p4和p5的失败。 - 案例2:所有
Promise都失败,返回一个AggregateError,包含所有错误信息。 - 案例3:
p1是唯一成功项,因此直接返回其值。
验证了any的核心特性:只要有一个成功就立即返回,只有全失败时才返回聚合错误。
4. Promise.allSettled
定义:
Promise.allSettled 接收一个 Promise 可迭代对象,并返回一个新的 Promise。新 Promise 在所有输入的 Promise 完成(无论成功或失败)后才完成,结果是一个包含每个 Promise 状态和结果的数组。
特点:
- 全部完成才结束:无论成功或失败,等待所有
Promise结束。 - 结果结构化:返回的数组包含每个
Promise的status(fulfilled或rejected)和value/reason。
适用场景:
- 需要收集所有任务结果(无论成功或失败)的场景(如批量上传文件,记录每个文件的状态)。
- 对错误容忍度高的场景(如统计多个独立操作的执行情况)。
示例:
// 案例1:部分成功,部分失败
Promise.allSettled([p1, p4, p5])
.then((results) => {console.log(results);});
/* 输出:
[
{ status: "fulfilled", value: "p1 resolve" },
{ status: "rejected", reason: "p4 reject" },
{ status: "rejected", reason: "p5 延迟 1s" }
]
*/
// 案例2:全部成功
Promise.allSettled([p1, p2, p3])
.then((results) => {console.log(results);});
/* 输出:
[
{ status: "fulfilled", value: "p1 resolve" },
{ status: "fulfilled", value: "p2 延迟2s" },
{ status: "fulfilled", value: "p3 延迟3s" }
]
*/
// 案例3:全部失败
Promise.allSettled([p4, p5])
.then((results) => {console.log(results);});
/* 输出:
[
{ status: "rejected", reason: "p4 reject" },
{ status: "rejected", reason: "p5 延迟 1s" }
]
*/
效果描述:
- 案例1:
p1成功,p4和p5失败,返回的数组包含所有状态和结果。 - 案例2:所有
Promise成功,返回的数组中每个对象的status为fulfilled。 - 案例3:所有
Promise失败,返回的数组中每个对象的status为rejected。
验证了allSettled的核心特性:无论成功或失败,都会等待所有Promise完成,并返回结构化的结果。
总结
| 方法 | 行为特点 | 适用场景 |
|---|---|---|
Promise.all | 全部成功才成功,任一失败立即失败 | 需要所有任务成功后继续 |
Promise.race | 第一个完成的决定结果(无论成功或失败) | 竞速逻辑或超时控制 |
Promise.any | 任一成功即成功,全失败返回聚合错误 | 多尝试中取第一个成功 |
Promise.allSettled | 等待所有完成,返回每个任务的详细状态 | 需要统计所有任务结果(无论成功或失败) |