We have two approaches:
// consider someFunction1() and someFunction2() as functions that returns Promises
Approach #1:
return [await someFunction1(), await someFunction2()]
Approach #2:
return await Promise.all([someFunction1(), someFunction2()])
My Team Leader said that both approaches ended up in the same solution (both functions executing in parallel). But, from my knowledge, the first approach would await someFunction1() to resolve and then would execute someFunction2.
So that's the question, is it really the same, or are there any performance improvements on second approach? Proofs are very welcome!
No, you should not accept that:
return [await someFunction1(), await someFunction2()];
Is the same as:
return await Promise.all([someFunction1(), someFunction2()]);
I should also note that await in the above return await is not needed. Check out this blog post to learn more.
They are different!
Let's determine the difference by inspecting how each of the two alternatives works.
[await someFunction1(), await someFunction2()];
Here, in an async context, we create an array literal. Note that someFunction1 is called (a function which probably returns a new promise each time it gets called).
So, when you call someFunction1, a new promise is returned, which then "locks" the async context because the preceding await.
In a nutshell, the await someFunction1() "blocks" the array initialization until the returned promise gets settled (by getting resolved or rejected).
The same process is repeated to someFunction2.
Note that, in this first approach, the two promises are awaited in sequence. There is, therefore, no similarity with the approach that uses Promise.all. Let's see why.
Promise.all([someFunction1(), someFunction2()])
When you apply Promise.all, it expects an iterable of promises. It waits for all the promises you give to resolve before returns a new array of resolved values, but don't wait each promise resolve until waiting another one. In essence, it awaits all the promises at the same time, so it is a kind of "non-sequential". As JavaScript is single-threaded, you cannot tell this "parallel", but is very similar in the behavior point of view.
So, when you pass this array:
[someFunction1(), someFunction2()]
You are actually passing an array of promises (which are returned from the functions). Something like:
[Promise<...>, Promise<...>]
Note that the promises are being created outside Promise.all.
So you are, in fact, passing an array of promises to Promise.all. When both of them gets resolved, the Promise.all returns the array of resolved values. I won't explain in all details how Promise.all works, for that, I suggest you checking out the documentation.
You can replicate this "non-sequential" approach by creating the promises before using the await. Like so:
const promise1 = someFunction1();
const promise2 = someFunction2();
return [await promise1, await promise2];
While promise1 is being waited, promise2 is already running (as it was created before the first await), so the behavior is similar to Promise.all's.
My Team Leader said that both approaches ended up in the same solution (both functions executting in parallel).
That is incorrect.
But, from my knowledge, the first approach would
await someFunction1()to resolve and then would execute someFunction2.
That is correct.
Here is a demonstration
const delay = (ms, value) =>
new Promise(resolve => setTimeout(resolve, ms, value));
async function Approach1() {
return [await someFunction1(), await someFunction2()];
}
async function someFunction1() {
const result = await delay(800, "hello");
console.log(result);
return result;
}
async function someFunction2() {
const result = await delay(400, "world");
console.log(result);
return result;
}
async function main() {
const start = new Date();
const result = await Approach1();
const totalTime = new Date() - start;
console.log(`result: ${result}
total time: ${totalTime}`);
}
main();
Result is:
hello
world
result: hello,world
total time: 1205
Which means that someFunction1 runs to completion first and then someFunction2 is executed. It is sequential
const delay = (ms, value) =>
new Promise(resolve => setTimeout(resolve, ms, value));
async function Approach2() {
return await Promise.all([someFunction1(), someFunction2()]);
}
async function someFunction1() {
const result = await delay(800, "hello");
console.log(result);
return result;
}
async function someFunction2() {
const result = await delay(400, "world");
console.log(result);
return result;
}
async function main() {
const start = new Date();
const result = await Approach2();
const totalTime = new Date() - start;
console.log(`result: ${result}
total time: ${totalTime}`);
}
main();
Result is:
world
hello
result: hello,world
total time: 803
Which means that someFunction2 finishes before someFunction1. The two are parallel.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With