Dealing with Promise.all() and a bunch of async functions
Recently I've been in the situation where I needed to resolve multiple async functions in paralel. The tricky part was that these functions were written using async/await approach and I was going to use Promise.all()
function to resolve all of the async requests at the same time, when all of them are resolved.
I'm not going to tell you much about Promises and async/await. I assume you have already gained proper knowledge regarding how to use them in your project.
The first approach
In order to handle such situation where I had a bunch of async functions I managed to put them all into an array and use it as a param of Promise.all()
function. Just like that:
1 2 3 4 5 6 | Promise.all([ await dispatch(fetchDataFromOneSource), await dispatch(fetchDataFromAnotherSource) ]) .then([data1, data2] => {}) .catch(error => console.log(error)) |
The code listing above is simplified in order to focus on the most important part.
As you can see, I'm using:
1 2 3 4 | [ await dispatch(fetchDataFromOneSource), await dispatch(fetchDataFromAnotherSource) ] |
as an input param of Promise.all()
. As we all know, async/await
approach is just a syntax sugar for Promises, so I expected to have all promises resolved when data is ready. It works perfectly fine, when all promises are resolved correctly. The then()
part is run and everyone is happy.
In my case, there were specific situations when one of the async functions should fail and this should prevent from running a callback of then()
part. I expected that catch()
will be invoked instead.
I was so wrong! All of it failed silently and except showing errors in the browser console nothing happened in the UI, while it should!
The second approach
At the time being I was rushed by the deadline and I've come up with a following solution:
1 2 3 4 5 6 7 8 | Promise.all([ await dispatch(fetchDataFromOneSource) .catch(handleError), await dispatch(fetchDataFromAnotherSource) .catch(handleError) ]) .then([data1, data2] => {}) .catch(handleError) |
It solved my problem but it was not the most elegant solution in the world. It bothered me a lot. Duplicated error handlers for each promise was not the most optimal solution in my case.
The final approach
Finally, after thinking for hours. I realised where was the mistake and how to make the previous version more elegant:
1 2 3 4 5 6 | await Promise.all([ dispatch(fetchDataFromOneSource), dispatch(fetchDataFromAnotherSource) ]) .then([data1, data2] => {}) .catch(error => console.log(error)) |
This also worked as previously, but the final piece of code looks better and it's less hackish now.
It's worth to mention, that running await Promise.all()
allows you to assign its output to a variable and maybe destructure it, if needed, and then proceed with next actions instead of running the then()
callback param.
Summary
By writing this article I wanted to clarify the statement I wrote on Twitter:
TIL: If any promise in Promise.all() will fail then catch assigned to Promise.all won’t work. You have to add catch to all promises in the array of promises used a Promise.all param. #javascript
— Piotr Nalepa (@sunpietro) March 17, 2020
I was wrong then. The catch will be invoked when the array of Promises contain the Promise objects itself, not the invokations of async functions.
I hope this article clarified it and it will help you solving issues in your projects.