前言
在前端异步开发中,Promise.all 是我们最常用的并发解决方案,但它存在致命短板:一个失败,全部失败。
实际业务中,很多场景下我们允许部分接口/任务失败,只需要等待所有任务执行完毕,统一收集成功和失败的结果,这就是 Promise.allSettled 的核心价值。
本文将从零讲解 Promise.allSettled,包含基础语法、实战案例、与其他 Promise 方法对比、手写源码、高频面试题,全程干货,可直接收藏学习、发布使用。
一、核心概念
1. 定义
Promise.allSettled() 是 ES2020 新增的 Promise 静态方法,用于并行执行多个 Promise 任务,等待所有任务全部执行完毕(无论成功/失败)后,统一返回所有任务的执行结果。
2. 核心特性
- 永不短路:不会因为某个 Promise 失败而终止执行
- 永不 reject:最终返回的 Promise 永远是 fulfilled 状态
- 全量收集:统一收集每一个任务的成功结果或失败原因
3. 语法格式
javascript
Promise.allSettled(iterable)
- 入参:可迭代对象(通常是 Promise 数组)
- 返回值:状态为 fulfilled 的 Promise,结果为结果对象数组
二、返回值结构详解
allSettled 返回的结果数组中,每一项都是固定格式的对象,分为成功和失败两种形态:
1. 任务成功(fulfilled)
javascript
{
status: 'fulfilled', // 固定标识:成功
value: 成功的结果数据 // 任务返回值
}
2. 任务失败(rejected)
javascript
{
status: 'rejected', // 固定标识:失败
reason: 失败的错误信息 // 错误原因/异常对象
}
三、基础实战案例
我们创建 3 个 Promise 任务(包含成功、失败场景),直观感受执行效果:
javascript
// 构造多个异步任务
const p1 = Promise.resolve("接口1请求成功");
const p2 = Promise.reject(new Error("接口2请求失败"));
const p3 = Promise.resolve("接口3请求成功");
// 执行allSettled
Promise.allSettled([p1, p2, p3]).then((res) => {
console.log("所有任务执行完毕:", res);
});
输出结果:
javascript
[
{ status: 'fulfilled', value: '接口1请求成功' },
{ status: 'rejected', reason: Error: 接口2请求失败 },
{ status: 'fulfilled', value: '接口3请求成功' }
]
可以看到:即使有任务失败,所有结果依然全部返回。
四、业务实用写法(筛选成功/失败数据)
实际开发中,我们需要单独筛选出成功的有效数据和失败的异常任务,封装通用处理逻辑:
javascript
async function batchRequest() {
const p1 = Promise.resolve({ id: 1, name: "数据1" });
const p2 = Promise.reject(new Error("数据2加载失败"));
const p3 = Promise.resolve({ id: 3, name: "数据3" });
// 等待所有任务完成
const resultList = await Promise.allSettled([p1, p2, p3]);
// 筛选成功的结果
const successData = resultList
.filter((item) => item.status === "fulfilled")
.map((item) => item.value);
// 筛选失败的任务
const errorData = resultList
.filter((item) => item.status === "rejected")
.map((item) => item.reason.message);
console.log("成功数据:", successData);
console.log("失败原因:", errorData);
}
batchRequest();
输出结果:
javascript
成功数据: [ { id: 1, name: '数据1' }, { id: 3, name: '数据3' } ]
失败原因: [ '数据2加载失败' ]
五、核心使用场景
Promise.allSettled 专门适配多任务并行、允许部分失败、需要全量结果统计的场景:
- 批量接口请求:页面初始化同时请求多个无关接口,个别接口失败不影响整体页面渲染
- 批量上传文件:多文件并行上传,统计哪些上传成功、哪些上传失败,支持重试
- 批量数据校验/提交:批量提交表单、批量处理数据,全部执行完毕后统一提示结果
- 数据埋点上报:多组埋点并行上报,无需因为单个上报失败中断整体逻辑
六、Promise 四大并发方法对比(重点面试考点)
很多面试会考察 all / allSettled / race / any 的区别,一张表彻底分清:
| 方法 | 执行特性 | 结束条件 | 适用场景 |
|---|---|---|---|
Promise.all |
短路失败 | 全部成功才成功,一个失败直接失败 | 多个接口强依赖,必须全部成功 |
Promise.allSettled |
无短路 | 所有任务执行完毕,永不 reject | 批量任务,允许部分失败,需统计全量结果 |
Promise.race |
竞速执行 | 谁先完成(成功/失败)就返回谁 | 接口超时拦截、竞速请求 |
Promise.any |
容错成功 | 一个成功就成功,全部失败才失败 | 多备用接口请求,只要一个成功即可 |
七、手写 Promise.allSettled(面试必刷源码)
面试高频手写题,实现核心逻辑:遍历所有 Promise、捕获成功/失败、全部完成后统一返回结果。
javascript
/**
* 手写 Promise.allSettled
* @param {Array} promiseArr Promise数组
* @returns {Promise} 包含所有任务结果的Promise
*/
function myAllSettled(promiseArr) {
// 返回一个新Promise
return new Promise((resolve) => {
const result = []; // 存储所有任务结果
let finishCount = 0; // 记录完成的任务数
const len = promiseArr.length;
// 兼容空数组情况
if (len === 0) {
resolve([]);
return;
}
// 遍历执行所有Promise
for (let i = 0; i < len; i++) {
// 统一转为Promise对象(兼容普通值)
Promise.resolve(promiseArr[i])
.then((value) => {
// 成功:存入fulfilled状态结果
result[i] = { status: "fulfilled", value };
})
.catch((reason) => {
// 失败:存入rejected状态结果
result[i] = { status: "rejected", reason };
})
.finally(() => {
// 每完成一个任务,计数+1
finishCount++;
// 所有任务执行完毕,返回结果
if (finishCount === len) {
resolve(result);
}
});
}
});
}
// 测试手写方法
const p1 = Promise.resolve("成功1");
const p2 = Promise.reject("失败2");
myAllSettled([p1, p2]).then(res => console.log(res));
源码核心思路:
- 遍历传入的所有异步任务,兼容普通值和 Promise 对象
- 每个任务单独捕获成功/失败,标记对应状态
- 通过计数器统计完成数量,全部完成后统一 resolve 结果
- 永远不会触发 reject,贴合原生 API 特性
八、高频面试问答题
1. 说一下 all 和 allSettled 的区别?
Promise.all有短路机制,一个任务失败,整体直接 reject,无法获取剩余任务结果;Promise.allSettled无短路,会等待所有任务执行完毕,统一收集成功、失败结果,返回的 Promise 永远为成功状态;all适用于任务强依赖场景,allSettled适用于批量容错场景。
2. allSettled 为什么不需要 catch 捕获?
因为 allSettled 本身设计为永不 reject,无论内部任务是否失败,最终返回的 Promise 一定是 fulfilled 状态,因此无需通过 catch 捕获异常。
九、总结
Promise.allSettled核心:全量执行、全量收集、永不报错;- 业务核心用途:批量异步任务、允许部分失败、需要统计执行结果;
- 面试重点:与 all/race/any 的区别、手写源码、应用场景辨析。
💡 建议收藏 :开发中遇到批量并行异步任务,优先考虑使用
allSettled替代all,容错性更强!