43

I am trying to run array of functions parallel and when everybody finishes I want work on that result. I am using promises. Now, I can put all functions in an array and can do Promise.all(array of functions) But I have array like

[[promise1, promise2], [promise3,promise4], [promise5,promise6]],

where each promise is promisified function. Promise reference document says parameter in Promise.all should be an iterable object and my array is iterable. But it is not working for me. I think It is executing [promise1, promise2] as a promise but not individual promise.

Can anybody please help me how I can achieve it or is there any better way ?

Mahendra Gunawardena
  • 1,956
  • 5
  • 26
  • 45
rovy
  • 2,481
  • 5
  • 18
  • 26
  • 1
    You need to [Merge/flatten a multidimensional array in JavaScript?](http://stackoverflow.com/questions/10865025/merge-flatten-a-multidimensional-array-in-javascript) so you just have a flat array of promises, as it requires. – Jeremy Mar 18 '16 at 21:55
  • `promise.all` takes an array of promise objects, created from the return values of functions you have called already. "Now, I can put all functions in an array and can do Promise.all(array of functions)" will not call the functions, and would resolve the returned promise with a reconstructed copy of the input array. – traktor Mar 19 '16 at 06:10

7 Answers7

49

You need to also call Promise.all for each array item:

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

Or directly:

// 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)))

This way the outer Promise.all gets the grouped promises in other 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
});
Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
17

You can just use Promise.all on all inner arrays, then on the outer array, to get a promise for the array of arrays:

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

Notice that Promise.all takes an array of promises, not an array of promise-returning functions.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • This is far and away the most elegant solution and in my opinion the BEST answer. I still have trouble wrapping my head around how .map(Promise.all, Promise) works but it does and it's beautiful. – Ian H Sep 25 '18 at 16:33
  • @IanH It's passing `Promise` as the [second argument to `map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map#Parameters), to provide the `this` value. It's the same as `.map(Promise.all.bind(Promise))` – Bergi Sep 25 '18 at 16:52
  • @Bergi thank you so much for taking time to respond I have spent the last few hours trying to research binding this to promise but I am still quite lost on how binding 'this' to 'Promise' allows Promise.all to return a promise to the "outer" Promise.all – Ian H Sep 25 '18 at 18:03
  • 1
    @IanH It's just that `Promise.all` is a method and therefore needs to be bound to `Promise` if passed as a callback. One could as well have written `Promise.all(promiseArrArr.map(promiseArr => Promise.all(promiseArr)))` – Bergi Sep 25 '18 at 18:06
  • Agreed, by far the best solution. The beauty of this solution is that it maintains the array depth of returned promises and does not flatten them out like other answers. The solution itself was a little difficult for me to comprehend at first, but once I understood it was so obvious. – Anshuman Manral Apr 08 '20 at 06:08
13

If you have fixed number of subarrays, I’d suggest to use this simple solution:

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

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

Several promises could also be parallelized like this:

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

If you have nested arrays of promises - try this:

const nestedResults = await Promise.all(
    nestedPromises.map(promises => Promise.all(promises))
);
k06a
  • 17,755
  • 10
  • 70
  • 110
12

One other option is to flatten your array of arrays into a single array that you can then use Promise.all() on directly. Straight from an example on MDN, you can do that like this using .reduce() and .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
});

This solution allows the original promises array to contain either individual promises or arrays of promises or any combination of the two. It's flexible in that way and it provides the results in a single flattened array of results that are in the same order as the original promises, but are flattened into a single array. Because Promise.all() requires that you pass it an array, the other solutions presented here require the input to be a strict array of arrays - that array cannot have any plain promises in it only arrays of promises. This solution is more flexible in that way and will accept either type of input.

For example, this will work just fine if the input happens to be like this:

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

See working demo: https://jsfiddle.net/jfriend00/dLsktyyn/


Or you might find it useful to have the flatten() function lying around for other uses:

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
});    

Edit

Or, Javascript arrays now have .flat() which will flatten the array one level for you:

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

Promise.all(promises.flat()).then(function(results) {
    // all done here
    // results are in a flattened array
});
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Well, the downside of this approach is that the structure of the value passed to the `then` handler (a flat array of values) is no longer isomorphic with the input structure (an array of an array of promises), so it's hard to match up which promise yielded which value. In that sense, @Bergi's approach would seem to be better. –  Mar 19 '16 at 05:01
  • @torazaburo - It depends upon what you want to do with the results. The results are fully in order with this approach so it's not like you can't tell which goes with which and they may be easier to navigate in a flat array. It's another option depending upon what you want. – jfriend00 Mar 19 '16 at 05:04
  • Added info about how this solution works on either an array of promises or an array of arrays of promises or any combination/mixture of the two and provides the output in a single flattened array of results in the same order as the original promises were. – jfriend00 Mar 19 '16 at 08:04
  • Is there any means of, "ignoring" the result of certain items in the return array? I need to empty a database table, then proceed to fill it with new contents, but I want to store the results of the record creation queries in variables. Can I execute it all in a single `Promise.all`? – Patrick Zawadzki Mar 08 '18 at 17:35
8

If you are looking to flatten the multi-array, and pend on all promises, you can now do:

const promiseAll = Promise.all(promiseArray.flat());
jhtong
  • 1,529
  • 4
  • 23
  • 34
1

You can use this approach:

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
});
Dmitriy
  • 3,745
  • 16
  • 24
1

Here is my snippet.

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));

And here is the result.

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

Zhong Ri
  • 2,556
  • 1
  • 19
  • 23