Reputation: 603
Promise.all is rejected if any of the elements are rejected. For example, if you pass in four promises that resolve after a timeout and one promise that rejects immediately, then Promise.all will reject immediately.
Let's discuss the following code snippet:
(async () => {
try {
const awaited = await Promise.all([
(() => {
console.log('1');
return Promise.reject('2');
})(),
(() => {
console.log('3');
return Promise.resolve('4');
})(),
]);
console.log(awaited);
} catch (error) {
console.log(error);
} finally {
console.log('5');
}
})();
All the promises in the code above are immediately resolved/rejected. I think that the code should execute this way:
Instead, I see a '3' also being logged. Why is it so?
Upvotes: 0
Views: 1154
Reputation: 817000
Instead, I see a '3' also being logged. Why is it so?
All the other answers try to explain how promises or the event loop work, but in fact it has nothing to do with those.
The argument you pass to Promise.all
is an array that contains the results of two function calls. Therefore both functions will be called before Promise.all
is called.
Both functions console.log
a value and then return a promise.
Promise.all
receives an array containing those promises. So before Promise.all
does anything, both functions have already been executed and logged 1
and 3
respectively.
Maybe it's easier to see you restructure your code to create the promises beforehand:
(async () => {
try {
const promise1 = (() => {
console.log('1');
return Promise.reject('2');
})();
const promise2 = (() => {
console.log('3');
return Promise.resolve('4');
})()
const awaited = await Promise.all([promise1, promise2]);
console.log(awaited);
} catch (error) {
console.log(error);
} finally {
console.log('5');
}
})();
Just like with any other function, all function arguments are evaluated before the function itself is called. The order of events is rather this:
2
is created.4
is created.Here is a simpler example: Note that nothing is "done" with the second argument passed to the function and yet the value is still logged, precisely because all arguments are evaluated first:
function process(arg) {
return 'Received ' + arg;
}
const result = process(
(() => {
console.log('1');
return 1;
})(),
(() => {
console.log('3');
return 2;
})(),
);
console.log(result)
Upvotes: 2
Reputation: 26615
The Promise.all()
does fast-fail. However, it doesn't abort the other Promises from finishes. This example with timeouts illustrates it a little better.
Promise.all([
Promise.reject(),
new Promise((resolve, reject) => setTimeout(() => { console.log('a'); resolve(); }, 500)),
new Promise((resolve, reject) => setTimeout(() => { console.log('b'); resolve(); }, 2000))
])
.then(() => console.log('done'))
.catch(() => console.log('fail'));
You can see that the Promise.all()
calls the catch()
almost immediately, and stops waiting for the results of the other, because it won't change the outcome. However, the other two Promises aren't aborted in any way.
If you want to abort them, you'd have to implement your own logic to do that.
Upvotes: 0
Reputation: 2244
This is due to the way that Promises are handled in JavaScript. It is asynchronous in nature and there is no security as to the execution order. Please note that this is not an error but rather a feature of the language.
I would suggest you read more on the issue in this SO question and about the event loop.
Upvotes: -1