依次执行 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的主要内容,如果未能解决你的问题,请参考以下文章