1

I try to understand how JS processes asynchronous methods and finally I have come to async/await. Trying to get a full insight, I have created this example:

async function first() {
  console.log(9);
  await Promise.resolve(2).then((r) => console.log(r));
  console.log(0);
  await Promise.resolve(3).then((r) => console.log(r));
}

async function second() {
  console.log(10);
  await Promise.resolve(4).then((r) => console.log(r));
  console.log(11);
  await Promise.resolve(5).then((r) => console.log(r));
}
first();
second();
const promise = Promise.resolve("new Promise");
promise.then((str) => console.log(str));
//The output: 
//9
//10
//2
//4
//new Promise
//0
//11
//3
//5

So, I have a question, why does it have such an order, and how JS's EventLoop works with async/await

I tried to create some other examples with similar syntax but the result is the same

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
Rcinos
  • 33
  • 4
  • Does this answer your question? [What is the relationship between event loop and Promise](https://stackoverflow.com/questions/46375711/what-is-the-relationship-between-event-loop-and-promise) – Konrad Jan 28 '23 at 22:43
  • I know how JS processes promises and other asynchronous methods, like setTimeout and setInterval, but it shows another answer, like await has a less priority in comparison with usual promises. Anyway, thanks for the help – Rcinos Jan 28 '23 at 22:48
  • Not a direct answer to your question, but [this talk](https://www.youtube.com/watch?v=cCOL7MC4Pl0&t=201s) is a fantastic talk about how the event loop works. – rburmorrison Jan 28 '23 at 22:50
  • *I know how JS processes promises and other asynchronous methods, like setTimeout and setInterval* - promises use microtask queue and `setTimeout` and `setInterval` use event loop, they are different – Konrad Jan 28 '23 at 22:56
  • `await Promise.resolve(2).then((r) => console.log(r));` - You are making 3 things here: 1. Add `promise(2) to the queue. 2. When promise(2) resolves add another task `(r) => console.log(r)`. 3. When `(r) => console.log(r)` resolves add another task which will continue the function execution ( what `await` does) – Konrad Jan 28 '23 at 22:59
  • Thus the counsel to never mix `await` and `then`... – Heretic Monkey Jan 28 '23 at 23:06
  • 2
    Nothing to see here apart from "independent asynchronous methods execute interleaved in arbitrary order". Does it matter? What were you trying to understand here? Do you understand how it works if you had written the two methods using only promise chaining and without `async`/`await`? – Bergi Jan 28 '23 at 23:07

2 Answers2

2

Here is a simplified time table of the most important evaluated expressions, the callstack at that time, the promise job queue (with promise reactions):

Callstack Evaluation Job Queue
Script first() -
Script>first console.log(9) -
Script>first Promise.resolve(2).then() (r=2)=>console.log(r)
Script>first await <pending> (r=2)=>console.log(r)
Script second() (r=2)=>console.log(r)
Script>second console.log(10) (r=2)=>console.log(r)
Script>second Promise.resolve(4).then() (r=2)=>console.log(r)

(r=4)=>console.log(r)
Script>second await <pending> (r=2)=>console.log(r)

(r=4)=>console.log(r)
Script promise = Promise.resolve("new Promise") (r=2)=>console.log(r)

(r=4)=>console.log(r)
Script promise.then((str)=>console.log(str)) (r=2)=>console.log(r)

(r=4)=>console.log(r)

(str="new Promise")=> console.log(str)
Job (r=2)=>console.log(r) (r=4)=>console.log(r)

(str="new Promise")=> console.log(str)
Job>anonym console.log(2) (r=4)=>console.log(r)

(str="new Promise")=> console.log(str)

resume first()
Job (r=4)=>console.log(r) (str="new Promise")=> console.log(str)

resume first()
Job>anonym console.log(4) (str="new Promise")=> console.log(str)

resume first()

resume second()
Job (str="new Promise")=> console.log(str) resume first()

resume second()
Job>anonym console.log("new Promise") resume first()

resume second()
Job resume first() resume second()
Job>first console.log(0) resume second()
Job>first Promise.resolve(3).then() resume second()

(r=3)=>console.log(r)
Job>first await <pending> resume second()

(r=3)=>console.log(r)
Job resume second() (r=3)=>console.log(r)
Job>second console.log(11) (r=3)=>console.log(r)
Job>second Promise.resolve(5).then() (r=0)=>console.log(r)

(r=5)=>console.log(r)
Job>second await <pending> (r=3)=>console.log(r)

(r=5)=>console.log(r)
Job (r=3)=>console.log(r) (r=5)=>console.log(r)
Job>anonym console.log(3) (r=5)=>console.log(r)

resume first()
Job (r=5)=>console.log(r) resume first()
Job>anonym console.log(5) resume first()

resume second()
Job resum first() resume second()
Job>first - resume second()
Job resume second() -
Job>second - -

Some points to highlight:

  • When a then method is executed on a promise that is in a fulfilled state, a job is added to a job queue. When the script has been executed to completion the first job in the promise job queue is extracted and executed.

  • Be aware that when a then method is executed this creates a new promise that is pending, even when then is called on a resolved promise. That pending promise will only resolve when the callback passed as argument has been executed, and this happens via a job (so, asynchronously).

  • After the expression following an await is executed, the async function's running state is saved, and the function returns. This running state will be restored by a job that is queued when the awaited promise resolves.

Hope this clarifies a few things.

trincot
  • 317,000
  • 35
  • 244
  • 286
0

This is:

async function first() {
  console.log(9);
  await Promise.resolve(2).then((r) => console.log(r));
  console.log(0);
  await Promise.resolve(3).then((r) => console.log(r));
}

Is the same as this:

function first() {
  console.log(9);
  return Promise.resolve(2).then((r) => console.log(r)).then(() => {
    console.log(0);
    return Promise.resolve(3).then((r) => console.log(r));
  });
}

function first() {
  console.log(9);
  return Promise.resolve(2).then((r) => console.log(r)).then(() => {
    console.log(0);
    return Promise.resolve(3).then((r) => console.log(r));
  })
}

function second() {
  console.log(10);
  return Promise.resolve(4).then((r) => console.log(r)).then(() => {
    console.log(11);
    Promise.resolve(5).then((r) => console.log(r));
  })
}
first();
second();
const promise = Promise.resolve("new Promise");
promise.then((str) => console.log(str));

Same with better numbering

function first() {
  console.log(1);
  return Promise.resolve('a').then((r) => console.log(4, r)).then(() => {
    console.log(7);
    return Promise.resolve('b').then((r) => console.log(9, r));
  })
}

function second() {
  console.log(2);
  return Promise.resolve('c').then((r) => console.log(5, r)).then(() => {
    console.log(8);
    Promise.resolve('d').then((r) => console.log(10, r));
  })
}
first();
second();
console.log(3)
const promise = Promise.resolve('e');
promise.then((str) => console.log(6, str));
Konrad
  • 21,590
  • 4
  • 28
  • 64