Promise.all: Order of resolved values

JavascriptPromiseEs6 Promise

Javascript Problem Overview


Looking at MDN it looks like the values passed to the then() callback of Promise.all contains the values in the order of the promises. For example:

var somePromises = [1, 2, 3, 4, 5].map(Promise.resolve);
return Promise.all(somePromises).then(function(results) {
  console.log(results) //  is [1, 2, 3, 4, 5] the guaranteed result?
});

Can anybody quote a spec stating in which order values should be in?

PS: Running code like that showed that this seems to be true although that is of course no proof - it could have been coincidence.

Javascript Solutions


Solution 1 - Javascript

Shortly, the order is preserved.

Following the spec you linked to, Promise.all(iterable) takes an iterable as a parameter and internally calls PerformPromiseAll(iterator, constructor, resultCapability) with it, where the latter loops over iterable using IteratorStep(iterator).

Resolving is implemented via Promise.all() Resolve where each resolved promise has an internal [[Index]] slot, which marks the index of the promise in the original input.


All this means the output is strictly ordered given the iterable you pass to Promise.all() is strictly ordered (for example, an array).

You can see this in action in the below fiddle (ES6):

// Used to display results
const write = msg => {
  document.body.appendChild(document.createElement('div')).innerHTML = msg;
};

// Different speed async operations
const slow = new Promise(resolve => {
  setTimeout(resolve, 200, 'slow');
});
const instant = 'instant';
const quick = new Promise(resolve => {
  setTimeout(resolve, 50, 'quick');
});

// The order is preserved regardless of what resolved first
Promise.all([slow, instant, quick]).then(responses => {
  responses.map(response => write(response));
});

Solution 2 - Javascript

As the previous answers have already stated, Promise.all aggregates all resolved values with an array corresponding to the input order of the original Promises (see Aggregating Promises).

However, I would like to point out, that the order is only preserved on the client side!

To the developer it looks like the Promises were fulfilled in order but in reality, the Promises are processed at different speeds. This is important to know when you work with a remote backend because the backend might receive your Promises in a different order.

Here is an example that demonstrates the issue by using timeouts:

Promise.all

const myPromises = [
  new Promise((resolve) => setTimeout(() => {resolve('A (slow)'); console.log('A (slow)')}, 1000)),
  new Promise((resolve) => setTimeout(() => {resolve('B (slower)'); console.log('B (slower)')}, 2000)),
  new Promise((resolve) => setTimeout(() => {resolve('C (fast)'); console.log('C (fast)')}, 10))
];

Promise.all(myPromises).then(console.log)

In the code shown above, three Promises (A, B, C) are given to Promise.all. The three Promises execute at different speeds (C being the fastest and B being the slowest). That's why the console.log statements of the Promises show up in this order:

C (fast) 
A (slow)
B (slower)

If the Promises are AJAX calls, then a remote backend will receive these values in this order. But on the client side Promise.all ensures that the results are ordered according to the original positions of the myPromises array. That's why the final result is:

['A (slow)', 'B (slower)', 'C (fast)']

If you want to guarantee also the actual execution of your Promises, then you would need a concept like a Promise queue. Here is an example using p-queue (be careful, you need to wrap all Promises in functions):

Sequential Promise Queue

const PQueue = require('p-queue');
const queue = new PQueue({concurrency: 1});

// Thunked Promises:
const myPromises = [
  () => new Promise((resolve) => setTimeout(() => {
    resolve('A (slow)');
    console.log('A (slow)');
  }, 1000)),
  () => new Promise((resolve) => setTimeout(() => {
    resolve('B (slower)');
    console.log('B (slower)');
  }, 2000)),
  () => new Promise((resolve) => setTimeout(() => {
    resolve('C (fast)');
    console.log('C (fast)');
  }, 10))
];

queue.addAll(myPromises).then(console.log);

Result

A (slow)
B (slower)
C (fast)

['A (slow)', 'B (slower)', 'C (fast)']

Solution 3 - Javascript

Yes, the values in results are in the same order as the promises.

One might cite the ES6 spec on Promise.all, though it's a bit convoluted due to the used iterator api and generic promise constructor. However, you'll notice that each resolver callback has an [[index]] attribute which is created in the promise-array iteration and used for setting the values on the result array.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionThorben CroiséView Question on Stackoverflow
Solution 1 - JavascriptEtheryteView Answer on Stackoverflow
Solution 2 - JavascriptBenny NeugebauerView Answer on Stackoverflow
Solution 3 - JavascriptBergiView Answer on Stackoverflow