Search code examples
javascriptasynchronousasync-await

order of execution in async/await


I have been studying promises, await and async functions. While I was just in the stage of learning promises, I stumbled upon this :

async function async1(){
  console.log('Async 1');
  await async2();  // *
  console.log('AFter Async 1');
}

async function async2(){
  console.log('FROM async 2');
}


async1();
console.log('Synchronous'); //**

Which results in:

Async 1
FROM async 2
Synchronous
AFter Async 1

How is the code jumping from * to **. How is microtask being used here ??


Solution

  • There's no microtask involved in the execution of the specific sequence you asked about (from * to **), although a microtask gets queued during that sequence (then run later).

    An async function is synchronous until the first await, uncaught exception, or return (including an implicit return where code execution just "falls off" the end of the function). At that point, the function returns a promise and synchronous execution continues from the point at which the function was called. If it was an exception or a return or implicit return (not an await), a microtask is queued to settle the promise from the function (rejecting it on exception or fulfilling it on return).

    I've highlighted the synchronous parts of that code in yellow:

    The code from the question, with highlighting on: 1. async1 and async2's empty parameter lists, the first console.log in each, and (in async1) the call to async2() but not the await on it; 2. The two statements at the end that aren't in any function.

    In your example, when you call async1 at the bottom, its synchronous portion runs (logging Async 1 and then calling async2, which logs FROM async 2 during its synchronous portion); having run async2 and gotten a promise from it, async1 reaches the await and returns its own promise; then synchronous execution continues from just after you called it, logging Synchronous.

    In this particular example, between the * and ** parts, a microtask for the fulfillment of async2's promise is queued. When the task executing the synchronous code in your example is done, that microtask is picked up from the microtask queue and executed — and that settles the promise from async1 that was waiting for it; async1's code continues, does its log, and then implicitly returns, so a microtask is queued to process that settlement (fulfillment in this case), and picked up from the microtask queue once the async2 microtask is done executing. But nothing is waiting for that promise, so the settlement doesn't have any apparent effect.