Several promises at once
Picture a cook who has to bake two pies. He can put the first one in the oven, wait for it, and only then put in the second. Or he can put both in at once: then he waits once instead of twice.
Two await lines in a row work the first way. The second timer is set only after the first one has fired:
function wait(ms, value) {
return new Promise((resolve) => setTimeout(() => {
console.log("ready", value);
resolve(value);
}, ms));
}
async function main() {
await wait(100, "a");
await wait(100, "b");
}
main();
// ready a
// ready bLet's see what happens:
maincallswait(100, "a")and waits at the firstawait.- After 100 milliseconds, the timer prints
ready a, and the promise is fulfilled. - Only now does the call
wait(100, "b")start, and another 100 milliseconds laterready bprints.
In total, about 200 milliseconds pass. When the second wait does not depend on the first, this order only wastes time. Both promises can be started at once and awaited together.
Promise.all
Promise.all takes an array of promises and returns one promise. That promise is fulfilled once every promise in the array is fulfilled, and its value is an array of their values. The values come in the order of the input array, not in the order the promises finished:
async function both() {
const [a, b] = await Promise.all([wait(200, "slow"), wait(100, "fast")]);
console.log(a, b);
}
both();
// ready fast
// ready slow
// slow fastLet's go step by step:
- Both
waitcalls run at once, beforePromise.all, and both timers start counting. - After 100 milliseconds, the
fasttimer fires and printsready fast. - After 200 milliseconds, the
slowtimer fires and printsready slow. - Now both promises are fulfilled, and
awaitgives the array["slow", "fast"].
In the result, "slow" comes first, as in the input array, even though it was ready later. That is why destructuring hands the values to their names without a mix-up. That is how a cook serves dishes in the order they were ordered, not in the order they are ready.
Promise.all itself starts nothing: the timers start counting when wait is called, before it. Promise.all only waits for promises that already exist. If the array is empty, the promise from Promise.all is fulfilled at once with the value [].
Interviewers like to ask this: how do you wait for several independent promises faster than one by one? The answer: create them all at once and wait for them with Promise.all.
The first rejection
If even one promise is rejected, the promise from Promise.all is rejected at once with its reason and does not wait for the rest:
async function failFast() {
try {
await Promise.all([wait(100, "a"), Promise.reject(new Error("b failed"))]);
console.log("all done");
} catch (e) {
console.log(e.message);
}
}
failFast();
// b failed
// ready aThe rejection of Promise.all is thrown at the await line, like the rejection of a single promise, so try…catch catches it. The all done line never prints, and the error appears before timer a fires.
Here the cook analogy stops working. A cook whose first pie burned can take the second one out of the oven too. Promise.all cancels nothing: timer a still prints ready a, but nobody gets its value any more.
You practice asynchronous code in the course's exercises too. You already wrote a retry with await and try…catch in the exercise before this lesson. After its quiz, an exercise on a promise with a time limit waits for you.
Common mistake
The promises are passed as separate arguments instead of one array:
Promise.all(wait(100, "a"), wait(100, "b")); // ✗TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator))Promise.all takes only its first argument and tries to walk through it like an array. The first argument is a promise, not an array, and it cannot be walked through element by element. The word object in the message means that promise. The tail in parentheses is the internal name of the way an object is walked through, and you can skip it. Wrap the promises in square brackets: Promise.all([wait(100, "a"), wait(100, "b")]).
Key points
- Two
awaitlines in a row wait in turn, while independent promises can be created at once and awaited withPromise.all. - The values from
Promise.allcome in the order of the input array, not in the order they were ready. - The very first rejection rejects the promise from
Promise.allat once, but the rest of the work is not canceled. Promise.alltakes one array, not promises as separate arguments.
Subscribe to take the course
The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.