Promise.All: Order of Resolved Values

Promise.all: Order of resolved values

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

How does promise all guarantee return order in es6

There's no great trick to it: Promise.all just remembers the index of the promise and saves that promise's result to the correct slot in the array it builds. It doesn't just use push to build the array (as that would indeed be chaotic).

Here's an example of doing it showing you very roughly what's going on under the covers:

function randomDelay(val) {
return new Promise(resolve => {
setTimeout(
resolve,
Math.random() * 1000,
val
);
});
}

// See: https://tc39.es/ecma262/#sec-performpromiseall
function fakeAll(iterable) {
return new Promise((resolve, reject) => {
const values = [];
let remainingElementsCount = 1;
let index = 0;
for (const value of iterable) {
const thisIndex = index; // Remember the index for this result
Promise.resolve(value) // To handle thenables and raw values
.then(val => {
console.log(`values[${thisIndex}] = ${JSON.stringify(val)}`);
values[thisIndex] = val; // <=== Notice use of `thisIndex`
--remainingElementsCount;
if (remainingElementsCount === 0) {
resolve(values);
}
}).catch(reject);
++remainingElementsCount;
++index;
}
--remainingElementsCount;
if (remainingElementsCount === 0) {
resolve(values);
}
});
}

// Pass in an iterable of promises, raw values, and thenables
fakeAll([
randomDelay("one"),
"two", // Raw value
randomDelay("three"),
{then(cb) { cb("four"); }}, // Synchronous thenable
{then(cb) { setTimeout(cb, 20, "five"); }}, // Asynchronous thenable
])
.then(results => {
console.log(results);
})
.catch(error => {
console.error(error);
});
.as-console-wrapper {
max-height: 100% !important;
}

How to sort promises in their resolved order not caring about order in arguments

You might be looking for

async function fulfillmentOrder(promises) {
const results = [];
await Promise.all(promises.map(promise =>
promise.then(value => {
results.push(value);
})
));
return results;
}

JS Promises resolve in order?

It is a bit confusing exactly what you're asking based on the code you show. I will attempt to explain what I think you might be asking.

Let's say you have a set of code that creates 10 promises that each resolve in a random time and they keep track of when they were launched and when they finished. You can then see the results and see that the array is in the order of the original array, even though they finished in a much different order:

let finishCntr = 0;

function randDelay(val) {
let t = Math.floor(Math.random() * 3000);
return new Promise(resolve => {
setTimeout(() => {
let finishSequence = finishCntr++;
resolve({ launchSequence: val, finishSequence, t });
}, t);
});
}

let values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

// create array of promises from the values array
let promises = values.map(launchSequence=> {
return randDelay(launchSequence);
});

// wait for all the promises to finish
Promise.all(promises).then(results => {
// log the final results
console.log(results);
}).catch(e => {
console.log(e);
});

Promise.all() is not resolving promises in the expected order

Promise.all does not impose any order on the promises, they fulfill when they fulfill

the third promise "rejects" well after the other two "resolve", so the fact that the first two are resolved before the last is rejected is to be expected

by the way, your first two promises are resolving the value undefined and outputting to the console - which is why you may think that Promise.all is doing something it ought not be doing

Do I need to compose my promises differently to see the behavior I'm expecting?

You wont get exactly the behaviour you are expecting, because you expect the promises to resolve in a particular order, the only way that can happen is for the promise to wait for the previous one to be fulfilled before it can fulfill, so the time would be cumulative, not in parallel, so in that case you wont get the rejection for 7.5 seconds rather than the 5 seconds you "expect"

How to make result of Promise all in order

Promise.all() returns an array of the results in the same order; they just won't resolve in order. You could return the response within your request promise, and...

const [result1, result2, result3] = await Promise.all([promise1, promise2, promise3]);

Or if you wanted to iterate over an array...

const results = await Promise.all([promise1, promise2, promise3]);

JavaScript: having trouble to implement my own Promise.all with iterable

But the challenge is with how to iterate through the iterable.

Just use for/of to iterate the iterable.

Current implementation is to get the length of if and doing it with a for loop via const length = promiseArray.length however only arrays have length property on it, other iterables or iterators like Set or Map.values() will not have that property available.

Just count how many items you get inside the for/of iteration.


Here's a more detailed explanation of the implementation below

If you look at the spec for Promise.all() it accepts an iterable as its argument. That means that it must have the appropriate iterator such that you can iterate it with for/of. If you're implementing to the spec, you don't have to check if it is an iterable. If not, the implementation will throw and thus reject with the appropriate error (which is what it should do). The Promise executor already catches exceptions and rejects for you.

As has been mentioned elsewhere, you could use Array.from() on the iterable to produce an actual array from it, but that seems a bit wasteful because we don't really need a copy of that data, we just need to iterate it. And, we're going to iterate it synchronously so we don't have to worry about it changing during iteration.

So, it seems like it would be most efficient to use for/of.

It would be nice to iterate the iterable with .entries() because that would give us both index and value and we could then use the index to know where to put the result for this entry into the resultArray, but it does not appear that the spec requires support of .entries() on the iterable so this implementation makes do with just a simple iterable with for (const p of promiseIterable). The code uses its own counter to generate its own index to use for storing the result.

Likewise, we need to produce as the resolved value of the promise we're returning an array of results that are in the same order as the original promises.

And, the iterable does not have to be all promises - it can also contain plain values (that just get passed through in the result array) so we need to make sure we work with regular values too. The value we get from the original array is wrapped in Promise.resolve() to handle the case of a plain value (not a promise) being passed in the source array.

Then, lastly since we are not guaranteed to have a .length property, there is no efficient way to know in advance how many items are in the iterable. I work around that in two ways. First, I count the items as we go in the cntr variable so when we're done with the for/of loop, we know how many total items there were. Then, as the .then() results come in, we can decrement the counter and see when it hits zero to know when we're done.

Promise.all = function(promiseIterable) {
return new Promise((resolve, reject) => {
const resultArray = [];
let cntr = 0;

for (const p of promiseIterable) {
// keep our own local index so we know where this result goes in the
// result array
let i = cntr;

// keep track of total number of items in the iterable
++cntr;

// track each promise - cast to a promise if it's not one
Promise.resolve(p).then(val => {
// store result in the proper order in the result array
resultArray[i] = val;

// if we have all results now, we are done and can resolve
--cntr;
if (cntr === 0) {
resolve(resultArray);
}
}).catch(reject);
}
// if the promiseIterable is empty, we need to resolve with an empty array
// as we could not have executed any of the body of the above for loop
if (cntr === 0) {
resolve(resultArray);
}
});
}

FYI, you can see some of this logic in the ECMAScript specification for Promise.all() here. Also, make sure to look at PerformPromiseAll.

How do I guarantee resolution order of multiple promises?

So to be clear, what you want to do here is kick off all the promises at once and display the results of each promise in a particular order as they come in, correct?

In that case, I'd probably do it like this:

let slow = new Promise((resolve) => {  setTimeout(function()  {    // Rather than log here, we resolve to the value we want to log    resolve('slow');  }, 2000, 'slow');});
let instant = new Promise((resolve) => { resolve('instant'); });
let quick = new Promise((resolve) => { setTimeout(function() { resolve('quick'); }, 1000, 'quick');});
// All Promises are now running. Let's print the results...
// First wait for the result of `slow`...slow.then((result) => { // Result received... console.log(result); // Now wait for the result of instant... instant.then((result) => { // Result received... console.log(result); // Now wait for the result of quick... quick.then((result) => { // Result received... console.log(result); }).then((result) => { // Done console.log('finished'); }); });});


Related Topics



Leave a reply



Submit