除了 Promise.all(),JavaScript 还提供了多个专门处理多个异步操作的 Promise 静态方法,适用于不同的业务场景(如“只要一个完成”“等待所有完成但保留失败信息”“按顺序执行”等)。以下是核心方法的详细说明:
一、Promise.race():“竞速模式”,取第一个完成的结果
Promise.race() 接收一个 Promise 可迭代对象(如数组),哪个异步操作先完成(无论成功或失败),就立即返回该操作的结果(成功值或失败原因),忽略其他未完成的操作。
核心场景:
- 异步操作的超时控制(如“接口请求 5 秒内没响应则提示超时”);
- 优先使用“最快可用的资源”(如同时请求多个 CDN,取第一个返回的资源)。
示例:实现接口请求超时控制
// 1. 模拟真实接口请求(假设可能耗时较久)
const fetchData = new Promise((resolve) => {
setTimeout(() => resolve("接口请求成功,返回数据"), 3000); // 3秒后成功
});
// 2. 模拟超时定时器(5秒后触发超时)
const timeout = new Promise((_, reject) => {
setTimeout(() => reject(new Error("请求超时,请重试")), 5000); // 5秒后失败
});
// 3. 竞速:谁先完成就用谁的结果
Promise.race([fetchData, timeout])
.then((data) => {
console.log("成功:", data); // 3秒后执行,输出:接口请求成功...
})
.catch((error) => {
console.error("失败:", error.message); // 若接口超过5秒,输出:请求超时...
});
注意:
- 一旦有一个操作完成,
Promise.race()会立即确定结果,其他未完成的操作会被“静默忽略”(但不会终止其内部执行,只是不再处理其结果)。
二、Promise.allSettled():“全量模式”,保留所有操作的结果
Promise.allSettled() 接收一个 Promise 可迭代对象,等待所有异步操作全部完成(无论成功或失败),最终返回一个包含所有操作结果的数组。每个结果对象包含两个属性:
status:操作状态,值为fulfilled(成功)或rejected(失败);value(仅成功时):异步操作的成功结果;reason(仅失败时):异步操作的失败原因。
核心场景:
- 需要完整知道所有异步操作的结果(成功/失败都要处理),而非“有一个失败就终止”;
- 批量任务的“结果汇总”(如批量上传文件,需要知道每个文件的上传状态)。
示例:批量处理异步操作并汇总结果
// 模拟3个异步操作(2个成功,1个失败)
const task1 = Promise.resolve("任务1成功");
const task2 = Promise.reject(new Error("任务2失败:网络错误"));
const task3 = new Promise((resolve) => setTimeout(() => resolve("任务3成功"), 1000));
// 等待所有任务完成,汇总结果
Promise.allSettled([task1, task2, task3])
.then((results) => {
// 遍历结果数组,分别处理成功和失败
results.forEach((result, index) => {
if (result.status === "fulfilled") {
console.log(`任务${
index + 1}成功:`, result.value);
} else {
console.error(`任务${
index + 1}失败:`, result.reason.message);
}
});
});
// 输出结果:
// 任务1成功:任务1成功
// 任务2失败:网络错误
// 任务3成功:任务3成功
与 Promise.all() 的区别:
| 特性 | Promise.all() | Promise.allSettled() |
|---|---|---|
| 结果触发条件 | 所有成功 或 第一个失败 | 所有操作全部完成(无论成败) |
| 结果形式 | 成功值数组 或 单个失败原因 | 包含所有操作状态的结果对象数组 |
| 错误处理 | 快速失败(一个失败则整体失败) | 不“失败”,仅在结果中标记失败 |
三、Promise.any():“择优模式”,取第一个成功的结果
Promise.any() 接收一个 Promise 可迭代对象,等待第一个“成功”的异步操作,并返回其成功结果;只有当所有操作都失败时,才返回一个包含所有失败原因的 AggregateError(聚合错误)。
核心场景:
- 优先使用“第一个成功的资源”(如同时请求多个接口,只要有一个返回有效数据就用);
- 避免因单个操作失败导致整体不可用(容错性比
Promise.race()更高)。
示例:优先获取第一个成功的接口数据
// 模拟3个接口请求(2个失败,1个成功)
const api1 = new Promise((_, reject) => setTimeout(() => reject(new Error("接口1超时")), 1000));
const api2 = new Promise((resolve) => setTimeout(() => resolve("接口2返回:用户数据"), 1500));
const api3 = new Promise((_, reject) => setTimeout(() => reject(new Error("接口3报错")), 2000));
// 取第一个成功的结果
Promise.any([api1, api2, api3])
.then((data) => {
console.log("成功获取数据:", data); // 1.5秒后执行,输出:接口2返回:用户数据
})
.catch((errors) => {
// 只有所有操作都失败时才执行
console.error("所有接口都失败:", errors.errors); // 包含所有失败原因的数组
});
与 Promise.race() 的区别:
| 特性 | Promise.race() | Promise.any() |
|---|---|---|
| 结果触发条件 | 第一个完成(无论成功或失败) | 第一个成功(忽略失败,直到全部失败) |
| 失败条件 | 第一个操作失败则整体失败 | 所有操作都失败才整体失败 |
| 适用场景 | 超时控制(关注“是否超时”) | 资源择优(关注“是否有成功”) |
四、串行处理多个异步操作(非静态方法,基于 then 链式调用)
上述方法均为并行处理异步操作(同时执行多个),但如果需要按顺序执行多个异步操作(前一个完成后再执行下一个),则需通过 Promise.then() 链式调用或 async/await 实现(本质仍是基于 Promise)。
示例:按顺序请求接口(依赖前一个接口的结果)
// 模拟按顺序请求:先获取用户ID,再用ID获取用户详情
const fetchUserId = () => Promise.resolve("user_123"); // 第一步:获取ID
const fetchUserDetail = (userId) => Promise.resolve({
id: userId, name: "Alice" }); // 第二步:用ID获取详情
// 链式调用实现串行
fetchUserId()
.then((userId) => {
console.log("第一步:获取到用户ID", userId);
return fetchUserDetail(userId); // 传递ID给下一个异步操作
})
.then((userDetail) => {
console.log("第二步:获取到用户详情", userDetail); // 输出:{ id: "user_123", name: "Alice" }
});
// 或用 async/await 简化(更直观)
const getuserInfo = async () => {
const userId = await fetchUserId();
const userDetail = await fetchUserDetail(userId);
console.log("用户详情:", userDetail);
};
getuserInfo();
总结:方法选择指南
根据业务场景选择合适的方法:
| 场景需求 | 推荐方法 |
|---|---|
| 所有操作成功才继续,一个失败则整体失败 | Promise.all() |
| 只要有一个操作完成(无论成败)就继续 | Promise.race() |
| 所有操作都完成,需要知道每个的成功/失败 | Promise.allSettled() |
| 只要有一个操作成功就继续,所有失败才报错 | Promise.any() |
| 多个操作需按顺序执行(依赖前一个结果) | then 链式调用 / async/await |