利用 Promise.race
实现对 Promise.all
的并发限制
要求:实现对
Promise.all
并发个数的限制
思路
- 用一个数组存放所有
Promise
任务,一个集合存放正在执行的Promise
任务 queue
函数,递归执行,当数组遍历完全后,退出queue
函数内部,array[i++]()
获取当前函数并执行,实例化出一个promise
,此时promise
开始运行- 将此
proimse
添加到executing
集合,且同时添加到result
数组中 - 当
promise
执行完成时,又会从executing
集合中删除,保证executing
集合中的promise
都是正在执行的任务 - 对比
executing
长度,如果长度大于等于限制,就用race
等一个任务执行完,再递归调用queue
,如果没有超出限制,就直接递归调用queue
- 最后,用
Promise.all
将result
数组中的所有结果输入到then
方法中,如果result
中有失败的promise
,则会终止后续的promise
function limitPromise(promiseFnList, start = 0, limit = 5) {
const result = []; // 存储所有的 Promise 任务
const executing = new Set(); // 正在执行的 Promise 对象
let i = start;
const queue = () => {
// 边界条件:如果 promises 长度为 0,或 i 到达末尾,就返回一个空的 promise
if (i >= promiseFnList.length) return Promise.resolve();
// 拿到一个 promise并运行,设置运行完成后从 executing 集合中删除
const p = promiseFnList[i++]().finally(() => executing.delete(p));
// 将运行中的 promise 添加到 executing 集合
executing.add(p);
// 并同时添加到 result 数组
result.push(p);
// 使用 Promise.race,每当 executing 数组中 promise 数量低于 limit,就实例化新的 promise 并执行
// const r = executing.size >= limit ? Promise.race(executing) : Promise.resolve();
// 递归,直到遍历完 promises
// return r.then(() => queue());
// 或者一步到位
return executing.size >= limit ? Promise.race(executing).then(() => queue()) : queue();
}
// 最后,用 Promise.all 将 result 中的结果输出,其会等待最后一批执行中的 promise 状态变为完成后才能进入 then 方法
return queue().then(() => Promise.all(result));
}
function fakePromise(url, time, success = true) {
return function() {
return new Promise((resolve, reject) => {
console.log('开始' + url);
setTimeout(() => {
if (success) resolve(`收到来自${url}的相应!`);
else reject(`${url}失败!`);
}, time * 1000);
});
}
}
const promiseFnList = [
fakePromise('1', 1, false),
fakePromise('2', 3),
fakePromise('3', 2),
fakePromise('4', 1),
fakePromise('5', 1),
fakePromise('6', 4),
fakePromise('7', 4),
fakePromise('8', 3),
fakePromise('9', 3),
fakePromise('10', 1),
];
// 从下标为 3 的项开始,最大并发数为 2
limitPromise(promiseFnList, 3, 2).then(res => console.log(res), err => console.log(err));