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
CodePudding user response:
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 whenthen
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, theasync
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.
CodePudding user response:
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));