305

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.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Thorben Croisé
  • 12,407
  • 8
  • 39
  • 50

3 Answers3

407

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));
});
Etheryte
  • 24,589
  • 11
  • 71
  • 116
  • 2
    How would an iterable not be strictly ordered? Any iterable is "strictly ordered" by the order it produces its values in. – Benjamin Gruenbaum Jan 21 '15 at 12:37
  • 1
    Note - Firefox is the only browser implementing iterables in promises correctly. Chrome will currently `throw` an excpetion if you pass an iterable to `Promise.all`. In addition I'm not aware of any userland promise implementation that currently supports passing iterables although many have debated it and decided against it at the time. – Benjamin Gruenbaum Jan 21 '15 at 12:41
  • @BenjaminGruenbaum `Object` doesn't have a default iteration behavior (because it isn't strictly ordered), meaning that if you use one along with giving it an iterable interface, the order might be ambiguous depending on the implementation. – Etheryte Jan 21 '15 at 13:09
  • An iterable is always strictly ordered - the order is just not well defined. I'm nitpicking here and you can say that the order is arbitrary but it's an order nontheless. – Benjamin Gruenbaum Jan 21 '15 at 14:34
  • 3
    @BenjaminGruenbaum Isn't it possible to have an iterable that produces two different orders upon being iterated twice? For example, a deck of cards that produces cards in random order when it is iterated? I don't know if "strictly ordered" is the right terminology here, but not all iterables have a fixed order. So I think it's reasonable to say that _iterators_ are "strictly ordered" (assuming that's the right term), but _iterables_ are not. – JLRishe Jan 21 '15 at 16:05
  • 4
    @JLRishe I guess you're right, it is indeed iterators that are ordered - iterables are not. – Benjamin Gruenbaum Jan 21 '15 at 17:38
  • 23
    It's worth noting that the promises don't chain. While you'll get the resolution in the same order, there's no guarantee about when the promises are acted on. In other words, `Promise.all` can't be used to run an array of promises in order, one after the other. The promises loaded into the iterator need to be independent of each other for this to work predictably. – Andrew Eddie Apr 09 '15 at 01:28
  • 1
    @AndrewEddie While for me it seems natural that that's the main point of using `Promise.all`, your comment is a good thing to point out. – Etheryte Oct 23 '15 at 12:00
  • These seems to also be true for $q in angular in case anyone needs to know. – Liam Middleton Feb 08 '18 at 10:53
110

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)']
Benny Code
  • 51,456
  • 28
  • 233
  • 198
35

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.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Weird , I saw a youtube video today that said that the output order is determined by the first who resolved ,then second, then.....I guess the video OP was wrong? – Royi Namir Nov 15 '15 at 08:10
  • 2
    @RoyiNamir: Apparently he was. – Bergi Nov 15 '15 at 12:45
  • @Ozil Wat? The chronological order of resolution does absolutely not matter when all promises fulfill. The order of values in the result array is the same as in the input array of promises. If it's not, you should switch to a proper promise implementation. – Bergi Sep 21 '17 at 14:12
  • Hi@Bergi +1. It means that the **individual promises** can be **resolved in any order**, (which doesn't matter because the purpose of all() is to get promises in parallel), what actually matters is that it returns an array with the results in the same order; **they just won't be resolved in order**, right? THANKS IN ADVANCE –  Jan 31 '23 at 14:28
  • 1
    @Coder23 The promises passed to `Promise.all` resolve whenever they want, by themselves, in any order. `Promise.all` does not, and can not, control them. They would resolve even if they were not passed to `Promise.all` at all. – Bergi Jan 31 '23 at 21:37
  • Great @Bergi, so the job of Promise.all() is just to **return us "the results"** of that promises in a **defined particular order**, right? –  Jan 31 '23 at 22:41