If a piece of JavaScript code contains setTimeout
, almost everyone knows that the code will be delayed (asynchronously), but if the code contains setTimeout
, await
and Promise resolve
at the same time, then can you still the right order of execution?
This is a front-end interview question on the Internet. The main knowledge point to be examined is the execution sequence of asynchronous async/await
, setTimeout
, Promise resolve
, which is the microtaks
and microtaks
we are going to discuss here:
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
console.log("async2");
}
console.log("script start");
setTimeout(function() {
console.log("setTimeout");
}, 0);
async1();
new Promise(function(resolve) {
console.log("promise1");
resolve();
}).then(function() {
console.log("promise end");
});
console.log("script end");
If you want to fully answer this question, you need to understand JavaScript's synchronous queue and asynchronous queue, i.e. the subdivided microtasks and macrotasks in the asynchronous queue.
We know that synchronous tasks will be executed first during the running process of JavaScript. If asynchronous code is encountered, the asynchronous code will be placed in the asynchronous queue, and the code in the asynchronous queue will be executed after the execution of the synchronous code is completed. For example the following code:
console.log(1)
setTimeout(()=>{
console.log(2)
}, 0)
console.log(3)
setTimeout(()=>{
console.log(4)
}, 0)
console.log(5)
The code first executes console.log(1)
on the first line. Continue to execute and encounter the first asynchronous code setTimeout
, at which point JavaScript will place the fragment code in the asynchronous queue (macrotasks
to be exact, which will be discussed later), and then continue to execute the synchronous code console .log(3)
, and then encounters a second setTimeout
, which is also placed in the asynchronous queue and continues to execute the code console.log(5)
. When all the synchronous codes are executed, go back and check whether there are any unexecuted tasks in the asynchronous tasks. If so, execute them in order, which is the setTimeout
code that prints 2 and 4 respectively.
So the result of executing the above code is:
1
3
4
undefined
2
4
First look at the following code:
console.log(1)
setTimeout(() => {
console.log(2)
}, 0);
new Promise(function(resolve) {
console.log("3");
resolve();
}).then(function() {
console.log("4");
});
console.log(5)
You must know that the synchronous code console.log(1)
and console.log(4)
will be executed first, but for the two asynchronous Promise.then
and setTimeout
, who will execute first?
Here we want to introduce the concepts of microtasks
and macrotasks
. Both microtasks
and macrotasks
are asynchronous tasks, but the difference is:
* microtasks
tasks take precedence over macrotasks
tasks, which means that when the synchronization task is completed, the tasks in microtasks
will be executed first, and then the tasks in macrotasks
will be executed.
* The execution of microtasks will take precedence over UI tasks (in fact, UI tasks belong to macrotasks
), which means that if you keep inserting tasks into microtasks
, the page will stop responding.
* Common microtasks
are: process.nextTick
, Promises
(where the Promises
constructor is synchronous), queueMicrotask
, MutationObserver
* Common macrotasks
are: setTimeout
, setInterval
, setImmediate
, requestAnimationFrame
, I/O
, UI rendering
So for the code above:
console.log(1)
setTimeout
and put it in macrotasks
Promise
executes the synchronous constructor console.log("3"); resolve()
(Promise's constructor is synchronously executed! Promise's constructor is synchronously executed! Promise's construction The function is executed synchronously!) and put then
into microstaks
console.log(5)
microstaks
, which is console.log("4")
in Promise.then
microstaks
, execute the task in macrostaks
, namely console.log(2)
of setTimeout
So the result is
javascript
1
3
5
4
undefined
2
Let's move on to the example:
async function async1(){
console.log(2)
await async2()
console.log(3)
}
async function async2(){
console.log(4)
}
console.log(1)
async1()
console.log(5)
Promise.resolve().then(() => console.log(6))
console.log(7)
Hmmm, let me guess:
console.log(1)
console.log(2)
console.log(4)
But how to deal with this await
? What about the code behind await
?
In fact, the async
function returns a Promise object. When the function is executed, once it encounters await, it will execute the method after await first, and then put all the following code into microtasks
and continue to execute Synchronous tasks, so here will put the following console.log(3)
into microtasks
after executing await async2()
in async1
.
Continue is:
* Put console.log(3)
into microtasks
, jump out of async1
and continue to execute the synchronous code console.log(5)
* When a Promise is encountered, synchronously execute the Promise's constructor, where the Promise constructor is empty.
* Put the code in then
of Promise
into microtasks
* Continue to execute synchronous code console.log(7)
* After the synchronous code is executed, start executing the code in microtasks
, the order is console.log(3)
in async1
, console.log(6)
in Promise.then
So the result is:
javascript
1
2
4
5
7
3
6
Well, the relevant concepts are explained, let's go back and look at the famous interview question:
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
console.log("async2");
}
console.log("script start");
setTimeout(function() {
console.log("setTimeout");
}, 0);
async1();
new Promise(function(resolve) {
console.log("promise1");
resolve();
}).then(function() {
console.log("promise end");
});
console.log("script end");
Here is the result!
console.log("script start")
setTimeout
and put it in the macrotasks macro taskconsole.log("async1 start")
in synchronous code async1
console.log("async2");
in async2 synchronously, and put the code after await
into microtasksPromise
, execute its constructor synchronously console.log("promise1");
and put the then
function after resmove
into microtasks
console.log("script end")
in the last line, the synchronization task is completed, check whether there is a task in microtasks
, and execute itasync1 await
in microtasks
console.log("async1 end");
microtasks
, console.log("promise end");
in Promise resolve
microtasks
is executed, check whether there are tasks in macrotasks
setTimeout
callback in macrotasks
console.log("setTimeout")
So the result is:
script start
async1 start
async2
promise1
script end
async1 end
promise end
undefined
setTimeout