Promise.all() vs Promise.allSettled() in JS: The little-known difference

I can’t believe some developers think they are interchangeable.

They are not.

all() and allSettled() are much more different than you think.

So let’s say I decided to singlehandedly create my own ambitious new music app to finally take down Spotify.

I’ve worked tirelessly to make several music deals and have all the backend data ready.

Now I’m creating the app’s homepage: a shameless carbon copy of Spotify showing off daily mixes, recently played, now playing, and much more.

All this data will come from different API URLs.

Of course I wouldn’t be naive enough to fetch from all the URLs serially and create a terrible user experience (or would I?)

JavaScript
async function loadHomePage() { const apiUrl = 'music.tariibaba.com/api'; const dailyMixesUrl = `${apiUrl}/daily`; const nowPlayingUrl = `${apiUrl}/nowPlaying`; // ❌ Serial loading const dailyMixes = await ( await fetch(dailyMixesUrl) ).json(); const nowPlaying = await ( await fetch(nowPlayingUrl) ).json(); return { dailyMixes, nowPlaying }; }

No I wouldn’t.

So instead I use use Promise.all() to fetch from all these URLs at once:

Much better, right?

JavaScript
async function loadHomePage() { const apiUrl = 'music.tariibaba.com/api'; const dailyMixesUrl = `${apiUrl}/daily`; const nowPlayingUrl = `${apiUrl}/nowPlaying`; // Promise.all() ?? await Promise.all([ fetch(dailyMixesUrl), fetch(nowPlayingUrl), ]); return { dailyMixes, nowPlaying }; }

Wrong.

I just made the classic developer error of forgetting to handle the error state.

Let me tell you something: When Promise.all() fails, it fails:

JavaScript
async function loadHomePage() { const apiUrl = 'music.tariibaba.com/api'; const dailyMixesUrl = `${apiUrl}/daily`; const nowPlayingUrl = `${apiUrl}/nowPlaying`; // Promise.all() ?? try { await Promise.all([ fetch(dailyMixesUrl), fetch(nowPlayingUrl), ]); } catch (err) { // ❌ Which failed & which succeeded? We have no idea console.log(`error: ${err}`); } return { dailyMixes, nowPlaying }; }

What do you when one of those requests fail? Am I just going to leave that UI section blank and not try again?

But look what Promise.allSettled() would have done for us:

JavaScript
async function loadHomePage() { const apiUrl = 'music.tariibaba.com/api'; const dailyMixesUrl = `${apiUrl}/daily`; const nowPlayingUrl = `${apiUrl}/nowPlaying`; // ... // Promise.all() ?? try { const promises = await Promise.allSettled([ fetch(dailyMixesUrl), fetch(nowPlayingUrl), // ... ]); const succeeded = promises.filter( (promise) => promise.status === 'fulfilled' ); const failed = promises.filter( (promise) => promise.status === 'rejected' ); // ✅ now retry failed API requests until succeeded } catch (err) { // We don't need this anymore! console.log(`error: ${err}`); } return { dailyMixes, nowPlaying }; }

Now I could easily set up a clever system to tirelessly retry all the failed requests until they all work.

JavaScript
async function loadHomePage() { const apiUrl = 'music.tariibaba.com/api'; const dailyMixesUrl = `${apiUrl}/daily`; const nowPlayingUrl = `${apiUrl}/nowPlaying`; // Promise.all() ?? const pending = [ () => fetch(dailyMixesUrl), () => fetch(nowPlayingUrl), ]; while (pending.length > 0) { const promises = await Promise.allSettled( pending.map((pending) => pending()) ); const newPending = []; promises.forEach((promise, index) => { if (promise.status === 'rejected') { newPending.push(pending[index]); } }); pending = newPending; } return { dailyMixes, nowPlaying }; }

When all() is better

This is great, but I need to update a lot of data when a user plays a song.

The song’s stream count, the user’s history, recently played…

But the database I’m using is horrible and doesn’t have support for transactions / batched writes.

I need to find a way to make sure I can update all the data in their separate locations at the exact same time.

Luckily, Promise.all() is useful this time.

It’s all or nothing.

JavaScript
async function runTransaction(updates) { // updates are a list of DB actions try { // store db state, somehow... await Promise.all(updates); } catch (err) { // intelligently revert to previous state, somehow... } }

With all() I’m confident that if a single updates fails, it’s over.

And now I can even bring my smart auto-retry system here, but this time everything is getting retried, after this reversal.

Final thoughts

Promise.all() — If one of us fails, we all fail.

Promise.allSettled() — You are all on your own.



Every Crazy Thing JavaScript Does

A captivating guide to the subtle caveats and lesser-known parts of JavaScript.

Every Crazy Thing JavaScript Does

Sign up and receive a free copy immediately.

Leave a Comment

Your email address will not be published. Required fields are marked *