Escape from the async/await hell or avoid the chained async tasks
First of all let's talk about async/await hell , and then we'll see how to avoid it
TL;DR
This is the async/await hell we're talking about, it's just as an example, there's more.
const user = await getUser(id);
const items = await getItems();
return {
user,
items,
};
Here we're fetching the specific user details and then fetching a list of items after. You may say, what's wrong with that?, the thing is that we're waiting to get the user details before getting the list of items, althought fetching the list of items doesn't depends on the user list. So why bother waiting if we could run them in parallel?. In addition that reduce the perfomance.
Now, consider this example:
(async () => {
const user = await getUser(id);
const items = await getItems();
return {
user
items
}
})()
We wrapped it using IIFE, but still this code is executed one by one
So what can we do to avoid that.
We have solutions:
we could fix it using something like
In this case, the user and items returns a promise so could await for the promise to fulfil when returning the value
const user = userPromise(id);
const items = itemsPromise();
return {
user: await whenUser,
items: await whenItems,
};
But I prefer using Promise.all it's a lot cleaner
const [user, items] = await Promise.all([getUser(id), getItems()]);
return { user, items };
simple, elegant 😄 and up to twice as fast because the Promise.all execute all of them concurrently.
You can learn more about Promise.all on MDN
Note: Worth noting that Promise.all will return either when all promises succeed or the first one rejects, whereas Promise.allSettled will wait until every promise has either resolved or rejected
Top comments (26)
Nicely put. One note:
await Promise.all()
will throw an exception if either promise rejects rather than returning.If you care about the actual resolve/rejection, take a look at
Promise.allSettled
, which doesn't throw on rejectsyes yes, it was on the note too
Promise.allSettled will wait until every promise has either resolved or rejected
Usually you will want the user to try again if some of then failed
I have to admit I am left wondering who would write code like this to begin with:
it defeats the whole point of async!
And of course anyone who's using async functions must perforce have read some intro on Promises (and I admit I had to read many about 20 times before I think I finally got it) will knowabout
Promise.all()
andPromise.allSettled()
as they indeed the standard means of waiting on several promises at once.I'd be cautious with this:
It is mostly true. But it does hide some asusmptions too. It's important to understand that an async function consumes time in two ways:
The difference is crucial because 1. remains blocking regardless of its async status (the JavaScript engine not supporting true parallelism or multthreading at all), while category 2. work isn't and actually forms the wellspring of true Promises (from the Browser to the JavScript engine).
And that is relevant to that comment, because it can happen that two async functions poorly written, when run in parallel yield little benefit at all. That would be exceptional of course and falls within the purvey of the claim ( "up to twice" and I said that mostly true, in fact I'll go one better, it's quite insightful, because in the best case the two functions have identical cost in time and then you'd halve the net wait, as long as their time cost is in category 2.)
The reason for that distinction is very relevant when a function is expensive because of JavaScript processing time. In that instance however, the setTimeout function becomes very important as a means of relaxing your expensive code's strangle-hold on the JavaScript engine.
I discuss some of that here:
dev.to/thumbone/deferring-to-the-u...
yes yes definitely true, learned some on your comments, thanks
Are you sure it's running in parallel?
At a high level, Node.js falls into the category of concurrent computation. This is a direct result of the single-threaded event loop being the backbone of a Node.js application. The event-loop repeatedly takes an event and then sequentially executes all listeners interested in that event. The event loop never runs two pieces of JavaScript in parallel.
bytearcher.com/articles/parallel-v...
concurrently to be exact
It doesn't run in parallel, that would require multiple threads. Promise.all executes promises concurrently.
yes, thanks, fixed it
good job
Nicely said.
Thanks 😊
Nicely explained.
Thank you Judicaël, interesting :)
thank you thank you
What about error handling in this case, while using multiple awaits we could have error handling on response of each result , what would happen if promise got rejected, response is not same as what we expect ?
so as mentioned Promise.all will asynchronously rejects with the value of the promise that rejected, whether or not the other promises have resolved
It’s actually not that hard to implement a promisify function of our own, how it works. dua to get married soon to someone you love
Yep! That's the rule of thumb if async requests are intependent then there is no harm in calling them in parallel.
they're not executed in parallel but concurrently
Thanks for correcting me! I googled and found the difference.
Cheers!