实现 Promise.allSettled
手写实现 Promise.allSettled 方法,等待所有 Promise 完成(无论成功或失败)并返回结果数组
问题
Promise.allSettled 是 ES2020 引入的方法,它接收一个 Promise 数组作为参数,等待所有 Promise 都完成(无论成功或失败),然后返回一个新的 Promise,该 Promise 的结果是一个数组,包含每个 Promise 的状态和结果。
与 Promise.all 不同的是:
Promise.all在任何一个 Promise 失败时就会立即 rejectPromise.allSettled会等待所有 Promise 完成,不会因为某个 Promise 失败而提前结束
解答
/**
* 实现 Promise.allSettled
* @param {Array} promises - Promise 数组
* @returns {Promise} 返回一个新的 Promise
*/
Promise.myAllSettled = function(promises) {
// 参数校验:确保传入的是可迭代对象
if (!Array.isArray(promises)) {
return Promise.reject(new TypeError('参数必须是一个数组'));
}
return new Promise((resolve) => {
const results = []; // 存储所有结果
let completedCount = 0; // 已完成的 Promise 数量
const total = promises.length;
// 处理空数组的情况
if (total === 0) {
resolve(results);
return;
}
// 遍历所有 Promise
promises.forEach((promise, index) => {
// 使用 Promise.resolve 包装,确保处理非 Promise 值
Promise.resolve(promise)
.then(value => {
// Promise 成功时的处理
results[index] = {
status: 'fulfilled',
value: value
};
})
.catch(reason => {
// Promise 失败时的处理
results[index] = {
status: 'rejected',
reason: reason
};
})
.finally(() => {
// 无论成功或失败,都增加计数
completedCount++;
// 当所有 Promise 都完成时,resolve 结果数组
if (completedCount === total) {
resolve(results);
}
});
});
});
};
使用示例
// 示例 1:混合成功和失败的 Promise
const promise1 = Promise.resolve(100);
const promise2 = Promise.reject('错误信息');
const promise3 = new Promise((resolve) => setTimeout(() => resolve('延迟结果'), 1000));
const promise4 = Promise.resolve(200);
Promise.myAllSettled([promise1, promise2, promise3, promise4])
.then(results => {
console.log(results);
// 输出:
// [
// { status: 'fulfilled', value: 100 },
// { status: 'rejected', reason: '错误信息' },
// { status: 'fulfilled', value: '延迟结果' },
// { status: 'fulfilled', value: 200 }
// ]
});
// 示例 2:包含非 Promise 值
Promise.myAllSettled([1, 2, Promise.resolve(3), Promise.reject(4)])
.then(results => {
console.log(results);
// 输出:
// [
// { status: 'fulfilled', value: 1 },
// { status: 'fulfilled', value: 2 },
// { status: 'fulfilled', value: 3 },
// { status: 'rejected', reason: 4 }
// ]
});
// 示例 3:空数组
Promise.myAllSettled([])
.then(results => {
console.log(results); // 输出:[]
});
// 示例 4:实际应用场景 - 批量请求
const urls = [
'https://api.example.com/user/1',
'https://api.example.com/user/2',
'https://api.example.com/user/3'
];
const requests = urls.map(url => fetch(url).then(res => res.json()));
Promise.myAllSettled(requests)
.then(results => {
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`请求 ${index + 1} 成功:`, result.value);
} else {
console.log(`请求 ${index + 1} 失败:`, result.reason);
}
});
});
关键点
-
返回值结构统一:无论 Promise 成功或失败,都返回包含
status字段的对象,成功时包含value,失败时包含reason -
使用 Promise.resolve 包装:通过
Promise.resolve(promise)确保能够处理非 Promise 值(如普通数值、字符串等) -
保持结果顺序:使用
results[index]而不是results.push(),确保结果数组的顺序与输入数组一致 -
计数器机制:使用
completedCount统计已完成的 Promise 数量,当等于总数时才 resolve -
永远 resolve:
Promise.allSettled返回的 Promise 永远不会 reject,即使所有输入的 Promise 都失败了 -
边界情况处理:需要处理空数组的情况,直接返回空结果数组
-
使用 finally:利用
finally方法统一处理成功和失败后的计数逻辑,代码更简洁
目录