How to run JavaScript promises in parallel
Last updated: September 27, 2022.
One of the great things about JavaScript promises is flexibility in how they are run.
Unlike callbacks, which are always executed sequentially (one after another), you have options for how to run multiple promises.
Promises provide you with several options for how you can run promises in parallel.
Table of contents
Creating promises
To demonstrate, we need multiple promises.
Here are three that will resolve successfully at different time points:
/* Creating three promises */ const promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve("Fast") }, 500); }); const promise2 = new Promise((resolve, reject) => { setTimeout(() => { resolve("Slow") }, 2000); }); const promise3 = new Promise((resolve, reject) => { setTimeout(() => { resolve("Middling") }, 1000); });
Running promises in parallel
Option 1: Individually
One way is to simply handle the results of each promise individually:
/* Handling the promises individually */ const promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve("Fast") }, 500); }); const promise2 = new Promise((resolve, reject) => { setTimeout(() => { resolve("Slow") }, 2000); }); const promise3 = new Promise((resolve, reject) => { setTimeout(() => { resolve("Middling") }, 1000); }); promise1 .then(res => console.log(res)) // "Fast" (after 0.5 seconds) .catch(err => console.log(err)); promise2 .then(res => console.log(res)) // "Slow" (after 2 seconds) .catch(err => console.log(err)); promise3 .then(res => console.log(res)) // "Middling" (after 2 seconds) .catch(err => console.log(err));
A feature of running promises in parallel individually like this is that the result of each promise is returned as soon as it is complete.
And, if a promise fails, it does not affect the other promises, since all are executing individually.
Option 2: All or nothing with Promise.all
Another option is to handle the promises together with Promise.all
. This is a good option if the promises you want to run are part of a larger process.
To handle the promises together, first store them in an array. Then, call the .all()
method on the Promise
object, passing in the array of promises.
You can then handle the result using .then
and .catch
syntax:
/* Handling promises together Promise.all() */ const promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve("Fast") }, 500); }); const promise2 = new Promise((resolve, reject) => { setTimeout(() => { resolve("Slow") }, 2000); }); const promise3 = new Promise((resolve, reject) => { setTimeout(() => { resolve("Middling") }, 1000); }); const myPromiseArray = [promise1, promise2, promise3]; Promise.all(myPromiseArray) .then(res => console.log(res)) // ["Fast","Slow","Middling"] (after two seconds) .catch(err => console.log(err));
Handling with async-await syntax
Promise.all
can also be nicely combined with async-await syntax:
/* Promise.all() with async-await syntax */ const promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve("Fast") }, 500); }); const promise2 = new Promise((resolve, reject) => { setTimeout(() => { resolve("Slow") }, 2000); }); const promise3 = new Promise((resolve, reject) => { setTimeout(() => { resolve("Middling") }, 1000); }); const myPromiseArray = [promise1, promise2, promise3]; async function handleResult() { try { let res = await Promise.all(myPromiseArray); console.log(res); } catch(e) { console.log(err); } } handleResult();
If a promise fails using Promise.all
One feature to be aware of when using Promise.all
is that it ‘fails fast’.
If a single promise in the array fails, error-handling is immediately executed. And the result of none of the other promises is returned:
/* Promise.all() 'fails fast' when a promise rejects */ const promise1 = new Promise((resolve, reject) => { setTimeout(() => { reject(new Error("Failed")); resolve("Fast") }, 500); }); const promise2 = new Promise((resolve, reject) => { setTimeout(() => { resolve("Slow") }, 2000); }); const promise3 = new Promise((resolve, reject) => { setTimeout(() => { resolve("Middling") }, 1000); }); const myPromiseArray = [promise1, promise2, promise3]; Promise.all(myPromiseArray) .then(res => console.log(res)) .catch(err => console.log(err)); // "Error: Failed" (after 0.5 seconds)
Option 3: All no matter what with Promise.allSettled
In some situations, you may find Promise.all
too strict, and want to handle the result of all promises even if one fails.
Promise.allSettled
was created for this purpose.
It works in the same way as Promise.all
, except Promise.allSettled
will return the result of all promises, even if one rejects:
/* Promise.allSettled() continues handling promises even if one rejects */ const promise1 = new Promise((resolve, reject) => { setTimeout(() => { reject(new Error("Failed")); resolve("Fast") }, 500); }); const promise2 = new Promise((resolve, reject) => { setTimeout(() => { resolve("Slow") }, 2000); }); const promise3 = new Promise((resolve, reject) => { setTimeout(() => { resolve("Middling") }, 1000); }); const myPromiseArray = [promise1, promise2, promise3]; Promise.all(myPromiseArray) .then(res => console.log(res)) .catch(err => console.log(err)); // Output after 2 seconds: // [ // { // "status": "rejected", // "reason": "Failed" // }, // { // "status": "fulfilled", // "value": "Slow" // }, // { // "status": "fulfilled", // "value": "Middling" // } // ]
Now, there is a difference in the output.
Rather than an array of return values, an array of objects is returned, containing information about whether each promise was fulfilled or reject and the return value for each.
To make this output a simple array, interpret .then
only, pushing each result into a new array. For rejected promises, undefined will be the value in the array:
/* Arrayifying the result of Promise.allSettled() */ const promise1 = new Promise((resolve, reject) => { setTimeout(() => { reject(new Error("Failed")); resolve("Fast") }, 500); }); const promise2 = new Promise((resolve, reject) => { setTimeout(() => { resolve("Slow") }, 2000); }); const promise3 = new Promise((resolve, reject) => { setTimeout(() => { resolve("Middling") }, 1000); }); const myPromiseArray = [promise1, promise2, promise3]; Promise.allSettled(myPromiseArray) .then(res => { let results = []; res.forEach(res => results.push(res.value)); console.log(results); // [ undefined, "Slow", "Middling" ] })
Summary
Promises provide you with several options for handling them in parallel. The option you choose should reflect the interconnectedness of the promises.
For promises that relate to independent tasks, simply handle each one individually. But if promises are interrelated, Promise.all
and Promise.allSettled
provide a better solution that takes into account this connectedness.
Use Promise.all
for a ‘fail fast’ solution and Promise.allSettled
to handle all results, regardless of outcome.
Related links
- OpenJavaScript: A beginner’s guide to JavaScript promises
- OpenJavaScript: What is asynchronous JavaScript?
- OpenJavaScript: Async-await: a modern syntax for asynchronous JavaScript
- MDN Web Docs: JavaScript > Standard in-built objects > Promise