如何为一系列承诺数组做promise.all?

Posted

技术标签:

【中文标题】如何为一系列承诺数组做promise.all?【英文标题】:How to do promise.all for array of array of promises? 【发布时间】:2016-07-05 19:50:19 【问题描述】:

我正在尝试并行运行函数数组,当每个人都完成时,我想要处理该结果。 我正在使用承诺。现在,我可以将所有函数放在一个数组中并且可以 Promise.all(函数数组) 但我有像

这样的数组
[[promise1, promise2], [promise3,promise4], [promise5,promise6]],

每个promise 都是promisified 函数。 Promise 参考文档说 Promise.all 中的参数应该是一个可迭代对象,我的数组是可迭代的。 但这对我不起作用。我认为它正在执行 [promise1, promise2] 作为一个承诺,而不是个人承诺。

任何人都可以帮助我如何实现它或有更好的方法吗?

【问题讨论】:

您需要Merge/flatten a multidimensional array in javascript?,这样您就可以根据需要拥有一组扁平的承诺。 谁对所有答案都投了反对票,请评论他们有什么问题。 promise.all 接受一个 promise 对象数组,这些对象是根据您已经调用的函数的返回值创建的。 “现在,我可以将所有函数放在一个数组中,并且可以执行 Promise.all(函数数组)”不会调用这些函数,而是会使用输入数组的重建副本来解析返回的 Promise。 【参考方案1】:

这是我的sn-p。

const a = Promise.resolve(1);
const b = Promise.resolve(2);
const c = Promise.resolve(3);
const d = Promise.resolve(4);

const e = Promise.all([a, b]);
const f = Promise.all([c, d]);

Promise.all([e, f])
    .then(res => console.log(res));

这是结果。

[[1, 2], [3, 4]]

【讨论】:

【参考方案2】:

另一种选择是将您的数组数组扁平化为一个数组,然后您可以直接使用Promise.all()。直接来自example on MDN,您可以使用.reduce().concat() 执行此操作:

var promises = [[promise1, promise2], [promise3,promise4], [promise5,promise6]];

Promise.all(promises.reduce(function(a, b)  return a.concat(b); , []))
  .then(function(results) 
    // all done here
    // results are in a flattened array
);

此解决方案允许原始的 Promise 数组包含单个 Promise 或 Promise 数组或两者的任意组合。它以这种方式很灵活,它以单个扁平结果数组的形式提供结果,这些结果与原始 promise 的顺序相同,但被扁平化为单个数组。因为Promise.all() 要求您向它传递一个数组,所以这里介绍的其他解决方案要求输入是一个严格的数组数组——该数组中不能有任何简单的承诺,只有承诺的数组。这种解决方案更灵活,可以接受任何一种输入。

例如,如果输入恰好是这样的,这将正常工作:

var promises = [promise1, promise2, [promise3, promise4], [promise5, promise6]];

查看工作演示:https://jsfiddle.net/jfriend00/dLsktyyn/


或者您可能会发现将 flatten() 函数用于其他用途很有用:

function flatten(arr) 
    return arr.reduce(function(a, b)  return a.concat(b); , []);


var promises = [[promise1, promise2], [promise3,promise4], [promise5,promise6]];

Promise.all(flatten(promises)).then(function(results) 
    // all done here
);    

编辑

或者,Javascript 数组现在有 .flat(),它将为您将数组展平一层:

const promises = [[promise1, promise2], [promise3,promise4], [promise5,promise6]];

Promise.all(promises.flat()).then(function(results) 
    // all done here
    // results are in a flattened array
);

【讨论】:

好吧,这种方法的缺点是传递给then处理程序的值的结构(一个平面数组)不再与输入结构同构(一个数组的数组)的承诺),所以很难匹配哪个承诺产生了哪个价值。从这个意义上说,@Bergi 的方法似乎更好。 @torazaburo - 这取决于你想对结果做什么。使用这种方法,结果完全符合要求,因此您不会分不清哪个与哪个相配,而且它们可能更容易在平面数组中导航。这是另一种选择,取决于你想要什么。 添加了有关此解决方案如何在一组承诺或一组承诺或两者的任何组合/混合上工作的信息,并在相同的单个扁平化结果数组中提供输出与最初的承诺一样。 有没有办法“忽略”返回数组中某些项目的结果?我需要清空一个数据库表,然后继续用新内容填充它,但我想将记录创建查询的结果存储在变量中。我可以在一个 Promise.all 中执行所有操作吗?【参考方案3】:

如果您希望展平多数组并等待所有承诺,您现在可以这样做:

const promiseAll = Promise.all(promiseArray.flat());

【讨论】:

【参考方案4】:

如果你有固定数量的子数组,我建议使用这个简单的解决方案:

const balancePromises = [...];
const symbolPromises = [...];

const [balances, symbols] = await Promise.all([
    Promise.all(balancePromises),
    Promise.all(symbolPromises)
]);

几个 promise 也可以像这样并行化:

const [
    balances,
    debt,
    name,
] = await Promise.all([
    this.contract.getBalanceOf(user),
    this.contract.getDebtOf(user),
    this.contract.getNameOf(user),
]);

【讨论】:

非常感谢!【参考方案5】:

您还需要为每个数组项调用Promise.all

const promise4all = Promise.all(
   promiseArray.map(function(innerPromiseArray) 
        return Promise.all(innerPromiseArray);
   )
);

或直接:

// Fix: Promise.all.bind is required because it seems like Promise.all
// implementation relies on "this"
const promise4All = Promise.all(promiseArray.map(Promise.all.bind(Promise)))
// or
// const promise4All = Promise.all(promiseArray.map(p => Promise.all(p)))

这样,外部Promise.all 在其他Promise.all 中获得分组承诺:

promise4all.then(function(promiseGroupResult) 
  // promiseGroupResult is the actual result of each promise group
  // and you'll be able to get each result this way: promiseGroupResult.somePropertyName
);

【讨论】:

很高兴知道我为什么投了反对票。好吧,所有答案都被否决了。就我而言,我已经提供了一个答案来处理 OP 解释的场景。如果以这种方式嵌套承诺是否是一个好的设计,那不是问题。 顺便说一句,没有理由这样做Array.from(arguments),承诺只用一个值来实现。 @Bergi 我已经修好了 当“直接”调用时,我总是得到结果Promise.all called on non-object,但是第一个代码sn-p 工作,谢谢。 @MatíasFidemraizer 简写符号 const promise4All = Promise.all(promiseArray.map(Promise.all)) 没有工作,抛出 Promise.all 在非对象上调用的错误。【参考方案6】:

您可以在所有内部数组上使用Promise.all,然后在外部数组上使用,以获得数组数组的承诺:

Promise.all(promiseArrArr.map(Promise.all, Promise)).then(arrArr => …)

请注意,Promise.all 接受一组承诺,而不是一组承诺返回函数。

【讨论】:

这无疑是最优雅的解决方案,在我看来也是最好的答案。我仍然无法理解 .map(Promise.all, Promise) 的工作原理,但它确实有效,而且很漂亮。 @IanH 将Promise 作为second argument to map 传递,以提供this 值。同.map(Promise.all.bind(Promise)) @Bergi 非常感谢您花时间回复我在过去的几个小时里一直在尝试研究如何将 this 绑定到 Promise,但我仍然对如何将 'this' 绑定到 'Promise' 感到困惑Promise.all 向“外部” Promise.all 返回一个承诺 @IanH 只是Promise.all 是一个方法,因此如果作为回调传递,则需要绑定到Promise。也可以写Promise.all(promiseArrArr.map(promiseArr => Promise.all(promiseArr))) 同意,迄今为止最好的解决方案。这个解决方案的美妙之处在于它保持了返回承诺的数组深度,并且不像其他答案那样将它们展平。解决方案本身对我来说一开始有点难以理解,但一旦我理解它就变得如此明显。【参考方案7】:

您可以使用这种方法:

var promisesArray = [
    [promise1, promise2],
    [promise3,promise4],
    [promise5,promise6]
].reduce(function (arr, item) 
    item.forEach(function (promise) 
       arr.push(promise);
    );
    return arr;
, []);
Promise.all(promisesArray).then(function () 
    // code
);

【讨论】:

以上是关于如何为一系列承诺数组做promise.all?的主要内容,如果未能解决你的问题,请参考以下文章

面试题系列---什么是Promise,解决什么问题?Promise.all解决了什么问题?实现原理?回调地狱?

多个顺序的 fetch() 承诺

Promise.all 和 Promise.race 有效地使所有承诺“得到处理”,这是一种记录在案的行为吗?

使用 promises 索引映射 Promise.all 输出

使用 Array.map 时嵌套承诺,使用 Promise.all 但仍然无法正常工作

是否有可能在 Promise.all 中捕获所有被拒绝的承诺? [复制]