手写实现Promise.all
七崽爱吃小饼干2026/03/02阅读 1
手写实现 Promise.all
手动实现一个**Promise.all**,它的核心作用是:接收一个 Promise 数组,全部成功才返回成功结果,有一个失败就立即返回失败。
一、先明确 Promise.all 规则
- 接收一个可迭代对象(通常是数组),每个元素是 Promise / 普通值
- 所有 Promise 都成功 → 返回 按顺序的结果数组
- 只要 有一个失败 → 立即 返回该失败原因(短路机制)
- 空数组 → 直接返回成功空数组
- 普通值会被当成 已成功的 Promise
二、手写实现代码
javascript
/**
* 手写 Promise.all
* @param {Array} promises - Promise 数组
* @returns {Promise} 最终结果
*/
function PromiseAll(promises) {
// 1. 返回一个新的 Promise
return new Promise((resolve, reject) => {
// 转成数组(处理可迭代对象)
const promiseArray = Array.from(promises);
const length = promiseArray.length;
// 存放最终结果
const result = [];
// 记录已完成的数量
let count = 0;
// 空数组直接 resolve
if (length === 0) {
resolve(result);
return;
}
// 遍历所有 promise
promiseArray.forEach((item, index) => {
// 用 Promise.resolve 包装,兼容普通值
Promise.resolve(item)
.then((res) => {
// **按顺序存放结果**
result[index] = res;
count++;
// 全部完成才 resolve
if (count === length) {
resolve(result);
}
})
.catch((err) => {
// 只要一个失败,立即 reject
reject(err);
});
});
});
}
三、核心关键点
1. 为什么要用 result[index] = res?
因为 Promise 是异步的,执行速度不一样,必须按传入顺序存结果,不能用 push。
2. 为什么用 count 计数?
因为结果是按索引放的,result.length 不准,必须用计数器判断是否全部完成。
3. 为什么用 Promise.resolve(item)?
兼容普通值(数字、字符串、对象等),把它们变成已决议的 Promise。
4. 失败机制
只要有一个 reject,立刻触发整体失败,不等待其他 Promise。
四、测试使用
javascript
// 测试 1:全部成功
const p1 = Promise.resolve(1);
const p2 = new Promise(resolve => setTimeout(() => resolve(2), 1000));
const p3 = 3;
PromiseAll([p1, p2, p3]).then(res => {
console.log(res); // [1, 2, 3]
});
// 测试 2:有一个失败
const p4 = Promise.reject("出错啦");
PromiseAll([p1, p4, p2]).catch(err => {
console.log(err); // "出错啦"
});
五、总结
- 返回新 Promise
- 遍历所有任务,用
Promise.resolve包装 - 按索引存结果,保证顺序
- 计数器判断是否全部完成
- 任一失败立即 reject