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.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));

版权

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