Promise.all 是 ES6 中 Promise 的一个静态方法,它接收一个可迭代对象(通常是数组)作为参数,并返回一个新的 Promise 实例。其行为符合 Promise/A+ 规范对 Promise 组合的要求,具体规则如下:
功能描述
- 输入:一个可迭代对象(如数组),其元素可以是任意值(包括普通值、thenable 对象或 Promise 实例)。
- 输出:一个新的
Promise实例,该实例的状态和结果由输入的所有元素共同决定。
执行逻辑
-
统一包装:
对于输入中的每个元素,都会通过Promise.resolve(element)将其转换为一个 Promise 实例(非 Promise 值会被包装为已兑现的 Promise)。 -
等待所有完成:
- 如果所有转换后的 Promise 都变为已兑现(fulfilled) ,则返回的 Promise 变为已兑现,其结果是一个数组,包含所有兑现值,且顺序与输入顺序完全一致。
- 如果任何一个转换后的 Promise 变为已拒绝(rejected) ,则返回的 Promise 会立即变为已拒绝,拒绝原因为第一个被拒绝的 Promise 的拒绝理由(忽略后续其他 Promise 的结果)。
-
空迭代对象处理:
如果传入的可迭代对象为空(如空数组),则返回的 Promise 立即以已兑现状态完成,结果为空数组[]。
错误处理
- 短路机制:只要有一个 Promise 失败,
Promise.all返回的 Promise 就会立即失败,不会等待其他未完成的 Promise(但那些未完成的 Promise 仍然会继续执行,只是其结果被忽略)。 - 异常传递:任何被拒绝的 Promise 的拒绝原因会直接作为返回 Promise 的拒绝理由。
符合 Promise/A+ 规范 Promise.all 手写实现,包含错误处理和空迭代对象的处理:
javascript
Promise.myAll = function(iterable) {
// 将可迭代对象转换为数组,若不可迭代则抛出 TypeError(与原生行为一致)
const items = Array.from(iterable);
// 处理空迭代对象:立即成功,返回空数组
if (items.length === 0) {
return Promise.resolve([]);
}
return new Promise((resolve, reject) => {
const results = new Array(items.length); // 按索引存储结果
let remaining = items.length; // 剩余未完成的 Promise 数量
items.forEach((item, index) => {
// 统一包装非 Promise 值,确保 .then 可用
Promise.resolve(item).then(
value => {
results[index] = value; // 保持原顺序
remaining--;
if (remaining === 0) {
resolve(results); // 全部成功,返回结果数组
}
},
reason => {
reject(reason); // 只要一个失败,立即拒绝
// 注意:一旦 reject,Promise 状态已定,后续的 resolve/reject 将被忽略
}
);
});
});
};
关键特性说明
- 空迭代对象:直接返回
Promise.resolve([]),符合规范。 - 错误处理:任一 Promise 失败立即调用
reject,后续完成不影响已定的拒绝状态。 - 保持顺序:通过预分配数组和按索引赋值,确保结果顺序与输入一致。
- 非 Promise 值:使用
Promise.resolve包装,使得普通值也能作为成功结果。 - 边界情况:
Array.from(iterable)会在参数不可迭代时抛出TypeError,与原生Promise.all行为一致。
测试示例
javascript
// 空数组
Promise.myAll([]).then(console.log); // []
// 混合值
Promise.myAll([1, Promise.resolve(2), 3]).then(console.log); // [1, 2, 3]
// 错误情况
Promise.myAll([Promise.reject('error'), Promise.resolve(1)])
.catch(err => console.error(err)); // 'error'
// 异步 Promise
const p1 = Promise.resolve(1);
const p2 = new Promise(resolve => setTimeout(() => resolve(2), 100));
const p3 = 3;
Promise.myAll([p1, p2, p3]).then(console.log); // 约100ms后输出 [1, 2, 3]