Code Synopsis logo

How to Handle Multiple JavaScript Promises

A Javascript Promise allows us to require specific actions after a process completes. They are usually used in combination with asynchronous functions to handle the success or failure result so our script can be productive instead of waiting.

Handling Multiple Promises

Javascript gives us four methods to handle multiple, concurrent Promises:

  • Promise.race - You just want the first response. This eager beaver fulfills with the first settled Promise whether it’s resolved or rejected. This is useful for cancelling a Promise with a timeout.
  • Promise.any - You want the first successful result. Promise.any doesn’t concern itself with rejections unless all Promises reject. In that case it returns an aggregate error object. This one is useful for calling redundant services to ensure you receive a successful response.
  • Promise.all - You need all requests to be successful, and you’d rather call the whole thing off if even one fails. If there’s a failure, it gives up waiting and provides the reason given by that first Promise to reject. If none of the Promises reject, it fulfills with an array of values from all resolved Promises.
  • Promise.allSettled - You want to see what all Promises return. So you’ll wait for all Promises settle: resolved or rejected. It returns an array of objects containing status along with either value or reason properties.

None of the above methods give you the result of each promise as it comes in. If that’s all you want, you don’t need a composite method. Just call the Promises separately. But what if you also want to know when all promises have completed?

Also, sometimes a Promise can hang in a pending state for a long time. One stalled Promise will hold back Promise.all or Promise.allSettled and prevent it from returning any response. What if you want to act on some of the responses?

We can accomplish both of these goals by attaching .then() and .catch() methods to each Promise before passing them in as arguments to one of the composite methods. This retrieves the response of each settled Promise without having to wait for any stalled Promise, and we still get our composite summary after all Promises have settled, if ever.

Here’s an example using all four methods. Notice the .then() and .catch() methods attached to each method:

// Defining the three promises const promise1 = new Promise((resolve, reject) => { setTimeout(() => { reject('Error 1'); }, 100); }); const promise2 = new Promise((resolve) => { setTimeout(() => { resolve('Success 2'); }, 200); }); const promise3 = new Promise((resolve, reject) => { setTimeout(() => { reject('Error 3'); }, 300); }); // Attach .then() and .catch() to each Promise promise1 .then(result => { console.log('Promise 1 resolved:', result); }) .catch(err => { console.log('Promise 1 rejected:', err); }); promise2 .then(result => { console.log('Promise 2 resolved:', result); }) .catch(err => { console.log('Promise 2 rejected:', err); }); promise3 .then(result => { console.log('Promise 3 resolved:', result); }) .catch(err => { console.log('Promise 3 rejected:', err); }); // Call the four composite methods Promise.any([promise1, promise2, promise3]) .then(result => { console.log('Promise.any resolved with:', result); }) .catch(err => { console.log('Promise.any rejected with:', err); }); Promise.allSettled([promise1, promise2, promise3]) .then(result => { console.log('Promise.allSettled resolved with:', result); }) .catch(err => { console.log('Promise.allSettled rejected with:', err); }); Promise.all([promise1, promise2, promise3]) .then(result => { console.log('Promise.all resolved with:', result); }) .catch(err => { console.log('Promise.all rejected with:', err); }); Promise.race([promise1, promise2, promise3]) .then(result => { console.log('Promise.race resolved with:', result); }) .catch(err => { console.log('Promise.race rejected with:', err); });

Here’s the output:

Promise 1 rejected: Error 1 Promise.all rejected with: Error 1 Promise.race rejected with: Error 1 Promise 2 resolved: Success 2 Promise.any resolved with: Success 2 Promise 3 rejected: Error 3 Promise.allSettled resolved with [ { "status": "rejected", "reason": "Error 1" }, { "status": "fulfilled", "value": "Success 2" }, { "status": "rejected", "reason": "Error 3" } ]

Because we added the .then() and .catch() methods, we’re getting the result from each promise as it comes in.

Pessimist Promise.all fulfills as soon as any Promise rejects. It won’t even see the successful results. That’s why we see Promise.all logged immediately after Promise 1.

Speedy Promise.race returns the first settled promise regardless of status. It’s just as fast as Promise.all (it only comes in afterwards because we called it in that order).

Optimist Promise.any waits for a successful response, so it fulfills once Promise 2 settles.

Scientist Promise.allSettled patiently waits to collect all of the data, which is why it fulfills only after all the results are in.

Related posts:

Helpful references:

Have feedback on this topic?