Promise.all 的并发限制

利用 Promise.race 实现对 Promise.all 的并发限制

要求:实现对 Promise.all 并发个数的限制

思路

  1. 用一个数组存放所有 Promise 任务,一个集合存放正在执行的 Promise 任务
  2. queue 函数,递归执行,当数组遍历完全后,退出
  3. queue 函数内部,array[i++]() 获取当前函数并执行,实例化出一个 promise,此时 promise 开始运行
  4. 将此 proimse 添加到 executing 集合,且同时添加到 result 数组中
  5. promise 执行完成时,又会从 executing 集合中删除,保证 executing 集合中的 promise 都是正在执行的任务
  6. 对比 executing 长度,如果长度大于等于限制,就用 race 等一个任务执行完,再递归调用 queue,如果没有超出限制,就直接递归调用 queue
  7. 最后,用 Promise.allresult 数组中的所有结果输入到 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));

版权

本作品采用 CC BY-NC-ND 4.0 授权。