javascript – Incorrect return sequence using async/await

Question:

I'm trying to understand how await / async works. I have read several opinions that come to say

async / await is nothing more than a sugar-syntax on Promises .

However, the result I'm getting collides head -on with this: I'm not able to explain the outputs of the following code (originally taken from here ).

function sleep( ms ) {
  return new Promise( resolve => setTimeout( resolve, ms ) );
}

async function demo( ) {
  console.log( 'Taking a break...' );
  await sleep( 2000 );
  console.log( 'Two seconds later' );
}

async function demo2( ) {
  console.log( 'demo2 -> before' );
  await demo( );
  console.log( 'demo2 -> after' );
}

function demo3( ) {
  console.log( 'demo3 -> before' );
  demo2( );
  console.log( 'demo3 -> after' );
}

demo3( );

The result obtained is:

demo3 -> before
demo2 -> before
Taking a break…
demo3 -> after
… 2 second pause …
two seconds later
demo2 -> after

However , the expected result is this one:

demo3 -> before
demo2 -> before
Taking a break…
two seconds later
demo2 -> after
demo3 -> after

In some way I don't understand, using async / await changes the order of the returns! I can't think of any way, using only callbacks and closures , that would cause that behavior.

From the outputs, it seems that the function demo3( ) returns before the function demo2( ) , which is simply impossible according to the classic execution mode that I know.

How does async / await actually work? Is it really just a facility to use Promises ? Am I messing myself up looking for los 3 pies del gato ?

Answer:

I'm going to remove the syntactic sugar from your code so it's clear what's being done:

//se queda como está
function sleep( ms ) {
  return new Promise( resolve => setTimeout( resolve, ms ) );
}

function demoNoSugar() {
  console.log( 'Taking a break...' );
  let promesa = sleep(2000);
  return promesa.then(() => console.log( 'Two seconds later' ));
}


function demo2NoSugar() {
  console.log( 'demo2 -> before' );
  let promesa = demoNoSugar();
  return promesa.then(()=> console.log( 'demo2 -> after' ));
}

function demo3NoSugar( ) {
  console.log( 'demo3 -> before' );
  demo2NoSugar( )
  console.log( 'demo3 -> after' );
}

demo3NoSugar( );

Steps:

  1. Run demo3NoSugar , which 'demo3 -> before' .
  2. Run demo2NoSugar , which 'demo2 -> before' .
  3. Run demoNoSugar , which puts 'Taking a break...' into the console.
  4. Calls sleep(2000) , which returns a promise that will resolve in 2 seconds. Before returning it, the order to write 'Two seconds later' when resolved is attached to demoNoSugar .
  5. To the resolution of that promise another command to write 'demo2 -> after' is added to demo2NoSugar and returned again.
  6. We're back at demo3NoSugar , which doesn't catch the promise, and just ends up typing 'demo3 -> after' to the console.
  7. The promise is resolved and the two attached messages are written.

Things to keep in mind:

A function that has an async always returns a Promise . Likewise, a function that returns a Promise can be considered to be async (and therefore can be used with await ):

async function devuelve1() {
  return 1;
}

function devuelve2() {
  return new Promise(res => res(2));
}

let p1 = devuelve1();
p1.then(r => console.log(r));


async function aux() {
  let r = await devuelve2();
  console.log(r);
}

aux();
Scroll to Top