串联执行一批承诺.完成Promise.all后,转到下一批

use*_*966 12 javascript batching node.js promise

我有一个包含promises数组的数组,每个内部数组可以有4k,2k或500个promise.

总共有大约60k的承诺,我也可以用其他值来测试它.

现在我需要执行Promise.all(BigArray[0]).

一旦第一个内部数组完成,我需要执行下一个Promise.all(BigArray[1]),依此类推,依此类推.

如果我尝试执行Promise.all(BigArray)它的抛出:

fatal error call_and_retry_2 allocation failed - process out of memory
Run Code Online (Sandbox Code Playgroud)

我需要按顺序执行每个promises,而不是并行执行它,我认为这就是Node正在做的事情.我不应该使用新的库,但我愿意考虑答案!

编辑:

这是一段代码示例:

function getInfoForEveryInnerArgument(InnerArray) {
    const CPTPromises = _.map(InnerArray, (argument) => getDBInfo(argument));
    return Promise.all(CPTPromises)
        .then((results) => {
            return doSomethingWithResults(results);
        });
}
function mainFunction() {
    BigArray = [[argument1, argument2, argument3, argument4], [argument5, argument6, argument7, argument8], ....];
    //the summ of all arguments is over 60k...
    const promiseArrayCombination = _.map(BigArray, (InnerArray, key) => getInfoForEveryInnerArgument(InnerArray));

    Promise.all(promiseArrayCombination).then((fullResults) => {
        console.log(fullResults);
        return fullResults;
    })
}
Run Code Online (Sandbox Code Playgroud)

jfr*_*d00 10

你的问题有点错误,可能会让这个问题的一些人和这个问题的前一版本感到困惑.您正在尝试串行执行一批异步操作,一批操作,然后在完成后执行另一批操作.使用promises跟踪这些异步操作的结果.Promise本身代表已经启动的异步操作."承诺"不是自己执行的.所以从技术上讲,你不会"连续执行一批承诺".您执行一组操作,使用promises跟踪其结果,然后在第一批完成后执行下一批操作.

无论如何,这是一个序列化每批操作的解决方案.

您可以创建一个我通常调用的内部函数next(),让您处理每次迭代.当承诺通过处理一个innerArray结算时,您next()再次调用:

function mainFunction() {
    return new Promise(function(resolve, reject) {
        var bigArray = [[argument1, argument2, argument3, argument4], [argument5, argument6, argument7, argument8], ....];
        //the summ of all arguments is over 60k...
        var results = [];

        var index = 0;
        function next() {
            if (index < bigArray.length) {
                getInfoForEveryInnerArgument(bigArray[index++]).then(function(data) {
                    results.push(data);
                    next();
                }, reject);
            } else {
                resolve(results);
            }
        }
        // start first iteration
        next();
    });
}
Run Code Online (Sandbox Code Playgroud)

这也会将所有子结果收集到结果数组中,并返回一个主承诺,其解析值是此结果数组.所以,你可以这样使用:

mainFunction().then(function(results) {
    // final results array here and everything done
}, function(err) {
    // some error here
});
Run Code Online (Sandbox Code Playgroud)

您还可以使用.reduce()设计模式连续迭代数组:

function mainFunction() {
    var bigArray = [[argument1, argument2, argument3, argument4], [argument5, argument6, argument7, argument8], ....];
    return bigArray.reduce(function(p, item) {
        return p.then(function(results) {
            return getInfoForEveryInnerArgument(item).then(function(data) {
                results.push(data);
                return results;
            })
        });
    }, Promise.resolve([]));
}
Run Code Online (Sandbox Code Playgroud)

这比第一个选项创建了更多的同时承诺,我不知道这对于如此大量的承诺是否是一个问题(这就是我提供原始选项的原因),但这个代码更清晰,概念使用方便对于其他情况.


仅供参考,我们为您提供了一些承诺附加功能.在Bluebird promise库(使用promises进行开发的一个很棒的库)中,它们具有Promise.map()以下功能:

function mainFunction() {
    var bigArray = [[argument1, argument2, argument3, argument4], [argument5, argument6, argument7, argument8], ....];
    return Promise.map(bigArray, getInfoForEveryInnerArgument);

}
Run Code Online (Sandbox Code Playgroud)


Dáv*_*zki 7

2020 年 10 月的回答。 Async/await 使其简短:只有 10 行代码 + JSDoc。

/**
 * Same as Promise.all(items.map(item => task(item))), but it waits for
 * the first {batchSize} promises to finish before starting the next batch.
 *
 * @template A
 * @template B
 * @param {function(A): B} task The task to run for each item.
 * @param {A[]} items Arguments to pass to the task for each call.
 * @param {int} batchSize
 * @returns {B[]}
 */
async promiseAllInBatches(task, items, batchSize) {
    let position = 0;
    let results = [];
    while (position < items.length) {
        const itemsForBatch = items.slice(position, position + batchSize);
        results = [...results, ...await Promise.all(itemsForBatch.map(item => task(item)))];
        position += batchSize;
    }
    return results;
}
Run Code Online (Sandbox Code Playgroud)

  • 这是一个很好的解决方案!我在代码中要做的唯一调整是使用“Promise.allSettled()”而不是“Promise.all()”,这样如果单个 Promise 被拒绝,批处理执行就不会失败。 (3认同)
  • 这次真是万分感谢。我进行了修改,通过跟踪在 while 循环中递增的“iterations”变量,允许任务函数具有像“foo(item, idx)”这样的签名。调用任务的行变成``Promise.all(itemsForBatch.map((item, idx) =&gt; task(item, iterations * batchSize + idx)))`` (2认同)