如何实现一个 Promise.all
更多描述
await Promise.all([1, 2, 3]);
//-> [1, 2, 3]
await Promise.all([1, Promise.resolve(2), 3]);
//-> [1, 2, 3]
await Promise.all([1, Promise.resolve(2)]);
//-> [1, 2]
await Promise.all([1, Promise.reject(2)]);
//-> Throw Error: 2
Issue 欢迎在 Gtihub Issue 中回答此问题: Issue 500
Author 回答者: shfshanyue
代码可在线调试: 如何实现一个 Promise.all - codepen
有一次头条面试,一道手写题目是:如何手写实现 promise.all
。
我从来没有想过要手写实现 promise.all 函数,稍微一想,大概就是维护一个数组,把所有 promise 给 resolve 了之后都扔进去,这有啥子好问的。没想到,一上手还稍微有点棘手。
先来看一个示例吧:
await Promise.all([1, Promise.resolve(2)]);
//-> [1, 2]
await Promise.all([1, Promise.reject(2)]);
//-> Throw Error: 2
- 传入一个 Iterable,但大部分情况下是数组,以下以数组代替
- 传入一个数组,其中可包含 Promise,也可包含普通数据
- 数组中 Prmise 并行执行
- 但凡有一个 Promise 被 Reject 掉,Promise.all 失败
- 保持输出数组位置与输入数组一致
- 所有数据 resolve 之后,返回结果
function pAll(_promises) {
return new Promise((resolve, reject) => {
// Iterable => Array
const promises = Array.from(_promises);
// 结果用一个数组维护
const r = [];
const len = promises.length;
let count = 0;
for (let i = 0; i < len; i++) {
// Promise.resolve 确保把所有数据都转化为 Promise
Promise.resolve(promises[i])
.then((o) => {
// 因为 promise 是异步的,保持数组一一对应
r[i] = o;
// 如果数组中所有 promise 都完成,则返回结果数组
if (++count === len) {
resolve(r);
}
// 当发生异常时,直接 reject
})
.catch((e) => reject(e));
}
});
}
为了测试,实现一个 sleep 函数
const sleep = (seconds) =>
new Promise((resolve) => setTimeout(() => resolve(seconds), seconds));
以下示例进行测试,没有问题
pAll([1, 2, 3]).then((o) => console.log(o));
pAll([sleep(3000), sleep(2000), sleep(1000)]).then((o) => console.log(o));
pAll([sleep(3000), sleep(2000), sleep(1000), Promise.reject(10000)])
.then((o) => console.log(o))
.catch((e) => console.log(e, "<- Error"));
Author 回答者: haotie1990
Promise.all = function (promises) {
const len = promises.length;
const result = new Array(len);
let countDone = 0;
return new Promise((resolve, reject) => {
if (len === 0) {
resolve(result);
}
for (let i = 0; i < len; i++) {
const promise = promises[i];
Promise.resolve(promise).then(
(data) => {
result[i] = data;
countDone++;
if (countDone === len) {
resolve(result);
}
},
(error) => {
reject(error);
},
);
}
});
};
Author 回答者: Vi-jay
function all(arr) {
return new Promise((resolve, reject) => {
const results = [];
arr.forEach(async (val) => {
Promise.resolve(val)
.then((data) => {
results.push(data);
if (results.length === arr.length) return resolve(results);
})
.catch((e) => reject(e));
});
});
}
Author 回答者: voezy
function all(arr) { return new Promise((resolve, reject) => { const results = []; arr.forEach(async (val) => { Promise.resolve(val) .then((data) => { results.push(data); if (results.length === arr.length) return resolve(results); }) .catch((e) => reject(e)); }); }); }
@Vi-jay 这个写法里,每个 promise 结束时间可能不一样,直接用 push 而不是数组循环索引去记录的话感觉可能会有问题。
Author 回答者: justorez
Promise.all = function (promises = []) {
return new Promise((resolve, reject) => {
let count = 0;
let values = new Array(promises.length);
const collect = (index) => (value) => {
values[index] = value;
++count === promises.length && resolve(values);
};
promises.forEach((promise, i) => {
if (promise instanceof Promise) {
promise.then(collect(i), reject);
} else {
collect(i)(promise);
}
});
});
};