Marking a function as async
doesn't mean that the code within that function will run asynchronously. If your code doesn't use await
or trigger any asynchronous calls, then your function will run entirely synchronously just as it would have without the async
keyword. Instead, async
means that your function can use await
within the function (to wait for another Promise to resolve), and that your function will always return a Promise. The code you've written within f1
and f2
both use synchronous for
loops, so both f1
and f2
run synchronously when called. To help with understanding, your code is more or less similar to doing (see code comments):
function f1(){ // note: No async
console.time("f1");
for(i=0;i<100;i++){}
console.timeEnd("f1");
return Promise.resolve(undefined); // returns a Promise that resolves immediately with `undefined`
}
function f2(){ // note, also not `async`
console.time("f2");
for(i=0;i<100;i++){}
console.timeEnd("f2");
return Promise.resolve(undefined);
}
async function main(){
console.time("main");
const p1 = f1(); // runs the for loop synchronously, returns a Promise which is stored in p1
// only once the first `for` in `f1` is complete and the entire `f1` function has finished executing then does the below code execute
const p2 = f2(); // runs the for loop synchronously, returns a Promise which is stored in p1
await Promise.all([p1, p2]); // wait for both `p1` and `p2` to resolve (p1 and p2 have already resolved by this point)
console.timeEnd("main");
}
main();
Another thing to note is that a Promise doesn't "run", it's more of an object that acts as a notification mechanism that tells your code once some sort of asynchronous operation has completed/failed. Promise.all()
allows you to wait for multiple asynchronous operations which are occurring parallelly to complete before your code uses the values produced by the promises and continues with your code execution. For example (see code comments):
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); // setTimeout() triggers a timer that then triggers `resolve` once completed. `setTimeout()` is asynchronous.
async function f1(){ // note: `async` because we `await` the Promise returned by `sleep()` below
console.time("f1");
await sleep(1000); // sleep for ~1s (await causes a pending Promise to be returned from `f1()`)
console.timeEnd("f1");
}
async function f2(){
console.time("f2");
await sleep(1000); // sleeep for ~1s
console.timeEnd("f2");
}
async function main(){
console.time("main");
const p1 = f1(); // runs f1() a stores the pending Promise in p1
// while the asynchronous `setTimeout()` is running, we queue another call to `setTimeout()` below by calling `f2`
const p2 = f2(); // runs `f2()` causing another setTimeout call to be queued and another pending Promise to be returned which is stored in `p2`
// By this point we have two timeouts running in parallel in the background, `p1` will resolve once the first is complete and `p2` will resolve once the second is complete
await Promise.all([p1, p2]); // wait for both pending promises, `p1` and `p2` to resolve (p1 and p2's setTimeout timers are running in the background and will resolve once completed)
console.timeEnd("main");
}
main();
If you were to remove the Promise.all()
and instead use:
async function main(){
console.time("main");
await p1();
await p2();
console.timeEnd("main");
}
Now your code will run sequentially, as p2()
is only invoked once the Promise returned by calling p1()
has resolved (as we're awaiting it with await
). The Promise returned by p1()
will only resolve once its associated setTimeout()
call is completed.