How can I change the following code so that both async operations are triggered and given an opportunity to run concurrently?
const value1 = await getValue1Async();
const value2 = await getValue2Async();
// use both values
Do I need to do something like this?
const p1 = getValue1Async();
const p2 = getValue2Async();
const value1 = await p1;
const value2 = await p2;
// use both values
Use .catch() and Promise.all()
Make sure you handle rejections correctly and you can safely use Promises.all() without facing unhandled rejections. (Edit: clarification per discussion: not the Error
unhandled rejection
but simply rejections that are not being handled by the code.Promise.all()
will throw the first promise rejection and will ignore the rest).In the example below an array of [[error, results], ...] is returned to allow ease of processing results and/or errors.
You may throw from within a catch() to stop waiting for all (and discard the results of the rest), however - you may only do it once per try/catch blocks so a flag has_thorwn need to be maintained and checked to make sure no unhandled errors happens.
TL;DR
Don't use the pattern in the question where you get the promises, and then separately wait on them; instead, use
Promise.all
(at least for now):While your solution does run the two operations in parallel, it doesn't handle rejection properly if both promises reject.
Details:
Your solution runs them in parallel, but always waits for the first to finish before waiting for the second.
If you just want to start them, run them in parallel, and get both results, it's just fine.(No, it isn't, keep reading...) Note that if the first takes (say) five seconds to complete and the second fails in one second, your code will wait the full five seconds before then failing.Sadly, there isn't currently
await
syntax to do a parallel wait, so you have the awkwardness you listed, orPromise.all
. (There's been discussion ofawait.all
or similar, though; maybe someday.)The
Promise.all
version is:...which is more concise, and also doesn't wait for the first operation to complete if the second fails quickly (e.g., in my five seconds / one second example above, the above will reject in one second rather than waiting five). Also note that with your original code, if the second promise rejects before the first promise resolves, you may well get a "unhandled rejection" error in the console (you do currently with Chrome v61), although that error is arguably spurious (because you do, eventually, handle the rejection). But if both promises reject, you'll get a genuine unhandled rejection error because the flow of control never reaches
const value2 = await p2;
and thus the p2 rejection is never handled.Unhandled rejections are a Bad Thing™ (so much so that soon, NodeJS will abort the process on truly unhandled rejections, just like unhandled exceptions — because that's what they are), so best to avoid the "get the promise then
await
it" pattern in your question.Here's an example of the difference in timing in the failure case (using 500ms and 100ms rather than 5 seconds and 1 second), and possibly also the arguably-spurious unhandled rejection error (open the real browser console to see it):
And here we reject both
p1
andp2
, resulting in a non-spurious unhandled rejection error onp2
:In a comment you've asked:
This has the same issues around promise rejection as your original code: It will wait until
p1
resolves even ifp2
rejects earlier; it may generate an arguably-spurious unhandled rejection error ifp2
rejects beforep1
resolves; and it generates a genuine unhandled rejection error if bothp1
andp2
reject (becausep2
's rejection is never handled).Here's the case where
p1
resolves andp2
rejects:...and where both reject:
I think this should work:
A more verbose example is below in case it helps in understanding: