JavaScript Concurrency: Promise allSettled
Welcome to the Promise allSettled lesson!
This lesson is shown as static text below. However, it's designed to be used interactively. Click the button below to start!
Promise.allwill reject as soon as any of the individual promises rejects. Sometimes we'd rather wait for all of the promises to finish, even if one or more of them rejects. For that, we can usePromise.allSettled.This presents a problem: what value should
Promise.allSettledfulfill with? WithPromise.all, the answer is pretty straightforward. If all of its promises fulfill,Promise.allalso fulfills with an array of the individual promises' values. If any of its promises reject,Promise.allimmediately rejects with the rejected promise's reason.For
Promise.allSettled, the equivalent would be an array of the promises' fulfilled values and rejection reasons. But that wouldn't give us a way to know which promises were fulfilled and which were rejected!We can see the problem by writing our own simplified implementation of
allSettled. It usesPromise.all, butcatches rejections before they get toPromise.all, which would stop execution. If a promise rejects, our function usescatchto return the reason for that promise. (Remember thatcatchturns a rejected promise into a fulfilled one.)>
function ourAllSettled(promises) {return Promise.all(promises.map(promise =>promise.catch(reason => reason)));}ourAllSettled([Promise.resolve(5),Promise.reject('rejection reason'),]);Async Result:
The
5here came from a fulfilled promise and the'rejection reason'came from a rejection, but we can't tell which is which. We need some "out of band data" or "metadata" to indicate which promises fulfilled and which rejected.We can wrap each of our promise results in an object with two properties. For fulfilled promises, the properties are
statusandvalue, since promises fulfill with values. For rejected promises, the properties arestatusandreason, since promises reject with reasons.We'll use
thento handle fulfilled promises andcatchto handle rejected promises. Here's our updated function.>
function ourAllSettled(promises) {return Promise.all(promises.map(promise =>promise.then(value => {return {status: 'fulfilled', value: value};}).catch(reason => {return {status: 'rejected', reason: reason};})));}Now
ourAllSettledreturns an array of promise results, each with a clear status.- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
ourAllSettled([Promise.resolve(5),Promise.reject('rejection reason'),]);Async Result:
ourAllSettlednow works like the built-inPromise.allSettledfunction! Here's what runningPromise.allSettledlooks like in practice:>
Promise.allSettled([Promise.resolve(1)]);Async Result:
>
Promise.allSettled([Promise.reject(new Error('oh no'))]);Async Result:
We've seen that
Promise.allrejects as soon as one of the underlying promises rejects.Promise.allSettledis different: it never rejects. The individual promises may reject, which will be indicated by astatusof'rejected', butPromise.allSettleditself always fulfills with an array of objects as we saw above.>
// Remember that allSettled's promise statuses are "fulfilled" or "rejected".Promise.allSettled([Promise.resolve(1),Promise.resolve(2),]).then(results =>results.map(result => result.status));Async Result:
{fulfilled: ['fulfilled', 'fulfilled']}>
// Remember that allSettled's promise statuses are "fulfilled" or "rejected".Promise.allSettled([Promise.resolve(1),Promise.reject(new Error('oh no')),]).then(results =>results.map(result => result.status));Async Result:
{fulfilled: ['fulfilled', 'rejected']}Here's a code problem:
Finish the
fulfilledValuesfunction below. It takes apromisesarray as an argument. It should return an array of the fulfilled values from the promises. Rejected promises should be ignored, since they don't have a fulfilled value.Some hints:
- Use
Promise.allSettled, which returns a promise containing an array of result objects. - Then remove the rejected results with
.filter(...). - Then get the values out of the remaining results with
.map(...).
function fulfilledValues(promises) {return Promise.allSettled(promises).then(results => {return results.filter(result => result.status === 'fulfilled').map(result => result.value);});}fulfilledValues([Promise.resolve('Amir'),Promise.reject(new Error("User doesn't exist")),Promise.resolve('Cindy'),]);Async Result:
- Goal:
{fulfilled: ['Amir', 'Cindy']}- Yours:
{fulfilled: ['Amir', 'Cindy']}
- Use
The
Promise.allandPromise.allSettledfunctions are used often in production web applications. You'll find them useful when you need to make multiple API requests, or write multiple changes to a database, or access multiple third-party web APIs quickly.Of the two,
Promise.allis more common because we usually want to fail immediately when something goes wrong. However,Promise.allSettledcan be useful when the promises are independent, or we need to know the result of each promise.