依次执行 Promise.all

Posted

技术标签:

【中文标题】依次执行 Promise.all【英文标题】:Execute Promise.all in series 【发布时间】: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 () 【参考方案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 感谢您提出这个问题。您提出的问题是这种方法无法正确处理未捕获的异常吗?我试图找出正确的方法来做到这一点;似乎***.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 表示异步操作的结果,它可以采用以下三种状态之一:

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

一旦 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')
    
    );

【讨论】:

以上是关于依次执行 Promise.all的主要内容,如果未能解决你的问题,请参考以下文章

JS手写面试题 --- Promise 以及相关方法的实现

处理多个承诺拒绝[重复]

HttpTrigger 使用python依次执行

依次执行 Promise.all

shell脚本中多个函数依次执行

依次执行自定义函数——Vue.js中的回调逻辑