This had me stumped for a while as to why the second iteration works — I was expecting the promises would run in serial so the time to completion would be longer. That isn't the case, because Promise.all takes an array of bare promises, not an array of functions resolving to promises.
With the following tuples of milliseconds and resolved values:
At ~999ms, push resolved values 2, 3, and 4 (because elements 3 and 4 have already resolved by this time)
Whether or not that's how Promise.all works under the hood, the end result is the same — all values, in the correct order, resolved in ~999 ms total time.
Yes, this was quite unintuitive the first time I saw something like that, but you can just loop over threads, promises, etc. and wait for each one to finish, since you're ultimately waiting for the one that runs the longest anyway, and even if it comes first, the others will then just resolve instantly.
I think what makes it doubly confusing is that a very common pattern for Promise.all is mapping over an array, so the callback to map takes a function resolving to a promise, even though what's directly being passed to Promise.all is the returned promises themselves.
But you could equally loop over the promises instead of the urls:
forawait(constdataofpromises){doSomething(data)}
Time taken: max latency of any one request.
Maybe this is just spelling out the obvious for some people but personally I still find it somewhat unintuitive, until you have that lightbulb moment 💡
After reading the introduction, I felt like doing this myself before continuing with the rest of the article; here's my result:
However, this looks very ugly, and keeping track of results manually feels very hacky. So here's a second iteration:
I love this solution
This had me stumped for a while as to why the second iteration works — I was expecting the promises would run in serial so the time to completion would be longer. That isn't the case, because
Promise.all
takes an array of bare promises, not an array of functions resolving to promises.With the following tuples of milliseconds and resolved values:
Your second iteration runs as following:
Whether or not that's how
Promise.all
works under the hood, the end result is the same — all values, in the correct order, resolved in ~999 ms total time.Console test runner
Yes, this was quite unintuitive the first time I saw something like that, but you can just loop over threads, promises, etc. and wait for each one to finish, since you're ultimately waiting for the one that runs the longest anyway, and even if it comes first, the others will then just resolve instantly.
I think what makes it doubly confusing is that a very common pattern for
Promise.all
is mapping over an array, so the callback tomap
takes a function resolving to a promise, even though what's directly being passed toPromise.all
is the returned promises themselves.cb
is(url: String) => Promise<any>
, butpromises
isArray<Promise<any>>
, notArray<(url: String) => Promise<any>>
.To fetch and do something with each resolved object in series, you'd do this:
Time taken: total latency of all requests.
But you could equally loop over the promises instead of the urls:
Time taken: max latency of any one request.
Maybe this is just spelling out the obvious for some people but personally I still find it somewhat unintuitive, until you have that lightbulb moment 💡
Isn't that a problem for error-handling, though? If you're still awaiting the 999ms Promise and the 800ms one rejects with an error, what happens?