【问题标题】:Execute Promise.all in series依次执行 Promise.all
【发布时间】:2016-09-08 20:46:13
【问题描述】:

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

总共有大约 60k 个 promise,我也可以使用其他值对其进行测试。

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

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

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

致命错误 call_and_retry_2 分配失败 - 进程内存不足

我需要按顺序执行每个 Promise,而不是并行执行,我认为这就是 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;
    })
}

【问题讨论】:

  • 尝试跟踪 60k promises 的状态听起来像是一场噩梦,我并不奇怪你的内存不足。听起来您需要进一步分解问题,或重新考虑架构。
  • 我同意@MikeMcCaughan。 60k Promises 听起来不合理,您的问题可能有更好的解决方案。
  • 这没有多大意义。如果你有一大堆 Promise,那意味着你的操作已经全部启动了。所以,这里没有连续执行。它们已经在并行执行。如果您只想知道所有承诺何时完成,那么请说出来,因为这听起来像是真正的问题。现在,在 node.js 中同时启动 60k 异步操作没有什么意义,所以这可能是你真正的问题。我认为您需要备份并向我们展示创建 60k 承诺的代码。这就是问题所在。
  • 投票结束为 "unclear what you're ask" 因为你不能序列化 60k 承诺,因为如果你已经承诺60k 异步操作,那么它们已经在并行运行,你不能序列化已经并行运行的东西。
  • 很清楚他在问什么;他想要一种方式来实现他的承诺,而不是并行,比如Promise.all ()

标签: javascript node.js promise


【解决方案1】:

Promise.all不行,你可以用Array.reduce来处理BigArray元素,一个一个:

BigArray.reduce((promiseChain, currentArray) => {
    return promiseChain.then(chainResults =>
        Promise.all(currentArray).then(currentResult =>
            [...chainResults, currentResult]
        )
    );
}, Promise.resolve([])).then(arrayOfArraysOfResults => {
    // Do something with all results
});

【讨论】:

    【解决方案2】:

    在 ES2017 中使用 async/await 非常简单:

    (async () => {
        for (let i = 0; i < BigArray.length; i++) {
            await Promise.all(BigArray(i));
        }
    })();
    

    【讨论】:

    • 你正在创建孤立的承诺。您无法在父 Promise 链中引用它们
    • @RobertMennell 感谢您提出这个问题。您提出的问题是这种方法无法正确处理未捕获的异常吗?我试图找出正确的方法来做到这一点;似乎stackoverflow.com/a/30378082/3352978 可能是一个可行的解决方案。
    【解决方案3】:

    Promise.all() 将检查您在 parallel 中作为参数传入的每个 Promise 结果,并在第一个错误时拒绝,或在完成所有 Promise 后解决。

    来自MDN

    Promise.all 从它所传递的可迭代对象中的所有 Promise 中传递一个值数组。值数组维护原始可迭代对象的顺序,而不是解决承诺的顺序。如果在可迭代数组中传递的东西不是承诺,它会通过 Promise.resolve 转换为一个。

    如果任何传入的 Promise 被拒绝,所有 Promise 立即以被拒绝的 Promise 的值拒绝,丢弃所有其他 Promise,无论它们是否已解决。如果传递的是空数组,则此方法立即解析。

    如果您需要依次执行所有 Promise,那么 Promise.all() 方法将适用于您的应用程序。相反,您需要找到一种迭代方法来解决您的承诺。这将是困难的; node.js 本质上是异步的,并且使用循环(据我所知和经验),在从循环内的 Promise 收到响应之前不会阻塞。

    编辑:

    存在一个名为 promise-series-node 的库,我认为它可以在这里为您提供相当多的帮助。由于您已经创建了 Promise,您可以将其传递给您的 BigArray

    promiseSeries(BigArray).then( (results) => {
       console.log(results);
    });
    

    在我个人看来,您从 60k+ 个 Promise 开始的方法不仅会花费大量时间,而且还会占用执行它们的系统上的资源(这就是您内存不足的原因)。我认为您可能需要为应用程序考虑更好的架构。

    Edit2,什么是承诺?:

    Promise 表示异步操作的结果,它可以采用以下三种状态之一:

    1. Pending:承诺的开始状态
    2. 已实现:成功操作所代表的承诺状态
    3. 已拒绝:由失败操作表示的承诺状态

    一旦 Promise 处于已完成或已拒绝状态,它们就不可更改。您可以链接承诺(非常适合避免重复回调),也可以嵌套它们(当关闭是一个问题时)。网上有很多很棒的文章,here is one I found to be informative

    【讨论】:

    • 你没有“执行承诺”。 Promise 表示已经启动的异步操作。您不能序列化已启动的 60k 异步操作。它们已经并行运行。因此,目前还不清楚这有什么帮助。
    • @jfriend00 从你在这篇文章中的所有 cmets 来看,这里对你来说没有任何意义。也许您可以发布答案并澄清一切。你说承诺不会执行,但在你对 op 的评论中你说它们会执行。
    • 我无法提供答案,因为这个问题没有意义。真正的答案是可能不会一次启动 60k 异步操作,但 OP 没有向我们显示任何代码,因此我们无法帮助解决真正的问题。
    • 您似乎不明白,如果 OP 已经有一系列承诺,那么这些异步操作已经在并行执行。你不执行承诺。承诺是已经开始的操作的未来结果的表示。您此时所能做的就是在它们全部完成时进行监控。可能没有任何情况下您希望 60k 异步操作都尝试同时运行,因为 node.js 不能很好地处理,所以这里的真正解决方案可能会备份几个步骤并停止启动 60k 异步操作一次。
    • 我很清楚promise是如何工作的,你是在争论空话。如果您阅读我的帖子,您会注意到我给了他改变方法的建议
    【解决方案4】:

    promise 库bluebird 提供了一个名为Promise.map 的辅助方法,该方法将数组或数组的promise 作为其第一个参数,并将其所有元素映射到结果数组,该数组反过来也被promisified。也许你可以尝试这样的事情:

    return Promise.map(BigArray, function(innerArray) {
      return Promise.all(innerArray);
    })
    .then(function(finishedArray) {
      // code after all 60k promises have been resolved comes here
      console.log("done");
    });
    

    但如前所述,这仍然是一项资源密集型任务,可能会消耗所有可用内存。

    【讨论】:

    • OP 说他们有一系列的承诺。我不清楚Promise.map() 将如何帮助解决这个问题。
    • map() 将并行解决承诺,但 Bluebird 的 mapSeries() 函数是解决 OP 问题的一个不错的解决方案。 bluebirdjs.com/docs/api/promise.mapseries.html
    • @Molomby 是的,这实际上是我的意图,我对 map() 的看法是错误的。我认为它将连续执行,但 mapSeries() 代替了这项工作。感谢您指出这一点。
    【解决方案5】:

    这里有好的答案Callback after all asynchronous forEach callbacks are completed

    function asyncFunction (item, cb) {
      setTimeout(() => {
        console.log('done with', item);
        cb(item*10);
      }, 1000*item);
    }
    
    
    
    let requests = [1,2,3].map((item) => {
        return new Promise((resolve) => {
          asyncFunction(item, resolve);
        });
    })
    
    Promise.all(requests).then(
    //  () => console.log('done')
        function(arr){
            console.log(arr)
             console.log('done')
        }
        );
    

    【讨论】:

      猜你喜欢
      • 2016-09-09
      • 1970-01-01
      • 2019-07-18
      • 2020-01-31
      • 2018-07-25
      • 2019-11-30
      • 2014-09-08
      • 2019-07-17
      • 1970-01-01
      相关资源
      最近更新 更多