To understand the order of JS execution first to understand the running mechanism of JS; Different from other blog articles, this article mainly combines personal summary, combined with knowledge points and topics, hoping to help you master knowledge points within an hour;Copy the code
Let’s have an appetizer and see if you got it right.
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('promise2');
});
console.log('script end'); The answer/* script start async1 start async2 promise1 script end async1 end promise2 setTimeout */
Copy the code
1. Js running mechanism
1.1 Single-threaded JavaScript
Js is single-threaded, event-based, non-blocking IO. Features: Processing I/O applications, not SUITABLE for CPU intensive applications. Note: The use of an event queue in the event cycle, at each point in time, the system will only process an event, even if the computer has multiple CPU cores, can not simultaneously process multiple events in parallel. Therefore, node.js defines a callback function for each input/output in an I/O-type application. Node.js automatically adds this callback function to the event polling processing queue. When the I/O operation is complete, this callback function will be triggered and the system will continue to process other requests.
- But shouldn’t a single thread execute sequentially from the top down?
- The following code output is out of order
function fn(){
console.log('start');
setTimeout(() = >{
console.log('setTimeout');
},0);
console.log('end');
}
fn()
// Start end setTimeout
Copy the code
1.2 Asynchrony in JavaScript
How is js synchronous asynchrony implemented? Javascript contains many functions to create asynchrony, such as seTimeout, setInterval, DOM events, Ajax, Promise, process.nextTick, etc
Because of the single thread, the code is executed from top to bottom, and all the code is put into'execute stack'In the implementation; > Add the callback function to an asynchronous functionTask queueInside (stored in order); > when'execute stack'After executing the code in theTask queueIn (get the stored functions in order); > will beTask queueThe function in'execute stack'In the implementation; < p style = "max-width: 100%; clear: both; min-height: 1em'Event loop';
Copy the code
- This analysis makes sense of the previous code;
- Look at this code again;
function fn() { setTimeout(()=>{ console.log('a'); }, 0); new Promise((resolve)=>{ console.log('b'); resolve(); }).then(()=>{ console.log('c') }); } setTimeout(()=>{console.log('a');} setTimeout(()=>{console.log('a'); }, 0); Store it to the task queue; " Moving on... , so why do you see a typed last;Copy the code
So the question is, how do B and C distinguish order? To look down
Immediate execution in 1.2.1 Promise and Async
We know that asynchrony in promises is embodied in then and catch, so the code written in promises is executed immediately as a synchronous task. In async/await, the code is executed immediately before an await occurs. \
So what happens when we get await?
1.2.2 What does await do
‘await’ is literally waiting. ‘await’ is waiting for an expression whose return value can be a Promise object or other value.
Many people think that await is waiting for a subsequent expression to complete before continuing to execute code. In fact, await is a sign of relinquishing a thread. The expression after await is executed once, the code after await is added to the microtask, and then the whole async function is jumped to execute the code after await. \
Because because async await itself is the syntactic sugar of the Promise +generator. So the code after await is microtask. So for the first interview question
**
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
Copy the code
Is equivalent to
**
async function async1() { console.log('async1 start'); Promise.resolve(async2()).then(() => { console.log('async1 end'); })}Copy the code
Example explanationasync function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2'); } async1(); So saidasyncInside, that is,awaitThe previous code was executed immediately; So the output order//async1 start
//async2
//async1 end
Copy the code
1.3 Macro and micro tasks
What is the position of two tasks in synchronous asynchrony? The two tasks are in macro queue and micro queue respectively. Macro queue and micro queue constitute task queue. Task queues place tasks on the execution stack for execution
1.3.1 Macro Tasks:
Macroqueue, macroTask, also known as Tasks
Macroqueue, macroTask, also known as Tasks. Callbacks from asynchronous tasks are placed in the Macro Task Queue, waiting to be called later.
- setTimeout
- setInterval
- SetImmediate (Exclusive to Node)
- RequestAnimationFrame (browser only)
- I/O
- UI Rendering (browser only)
The common setTimeout should be memorized, and the rest would be nice if you could remember them all
1.3.2 Microtasks:
Microqueues, microtasks, also known as Jobs
Microqueues, microtasks, also known as Jobs. The callbacks of asynchronous tasks enter the Micro Task Queue and wait for subsequent calls. These asynchronous tasks include:
- Process. nextTick (Node only)
- Promise
- Object.observe
- MutationObserver
Make sure to remember common promises, and if you can remember everything else, it’s best
The whole period is strung together
- Execute global Script synchronization code, some of which are synchronous statements, some of which are asynchronous statements (setTimeout, etc.);
- After executing the global Script code,
Execution stack
The Stack will clear;- from
The queue
Retrieves the callback task located at the top of the queue and drops it intoExecution stack
Stack. After the execution is completeThe queue
Length minus 1;- Continue to loop out the location
The queue
The task is put intoExecution stack
Stack, and so on, all the way to all the way to theMicro tasks
The execution is complete. Note that if you are executingMicro tasks
In the process of, again producedMicro tasks
, then it will join toThe queue
Will also be called during this cycle;The queue
All of theMicro tasks
All done, and nowThe queue
Is an empty queue,Execution stack
Stack is also empty;- Take out the
Macro queue
The task inExecution stack
Stack execution;- After the execution,
Execution stack
The Stack is empty.- Repeat steps 3-7;
That is a complete cycle of events
Execute first in the stack (i.e. in the main function); Temporarily store macro and microqueues in order; < p style = "max-width: 100%; clear: both; min-height: 1em; < span style = "max-width: 100%; clear: both; min-height: 1em; Then repeat the loopCopy the code
1.4 Interpretation of interview questions
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('promise2');
});
console.log('script end'); The answer/* script start async1 start async2 promise1 script end async1 end promise2 setTimeout */
Copy the code
1. First, the event loop starts from the macroTask queue. At this point, there is only one script task in the macroTask queue. When a task source is encountered, the task is first distributed to the corresponding task queue.
2. Then we see that we define two async functions, and then we see the console statement, and we print script start. After output, script tasks continue to execute, and setTimeout, as a macro task source, will first distribute its tasks to the corresponding queue
3. The script task continues and executes the async1() function. As described above, the code before await in async function is executed immediately, so async1 start is immediately printed. When await is encountered, the expression after await is executed once, so async2 is then output, console.log(‘async1 end’) is added to the Promise queue in microTask, The async1 function is then jumped out to execute the following code
4. The script task continues and encounters a Promise instance. Since the function in a Promise executes immediately, subsequent.then packets are distributed to the MicroTask Promise queue. Therefore, promise1 is printed and resolve is executed to allocate promisE2 to the corresponding queue
5. Script continues until script end is printed, and the global task is complete. According to the above, after each macro task is executed, the presence of Microtasks is checked; If so, execute Microtasks until the Microtask Queue is cleared. So after the script task is finished, the search starts to clear the microtask queue. In this case, the Promise queue has two tasks async1 end and promise2, so asynC1 end, promise2 is output in sequence. The first round of the loop is complete when all Microtasks have been executed
6. The second cycle starts again with the macro task queue. At this point, there is only one setTimeout in the macro task. You can take it out and output it directly. At this point, the whole process ends
Summarize the solution idea:1.> < span style = "max-width: 100%; clear: both; min-height: 1em; < p style = "max-width: 100%; clear: both; min-height: 1emasync/awaitandPromiseAssigned to the microqueue of the macro queue; > when it encounterssetTimeout"To the next macro queue2.> executes async1(), as described earlierasyncFunction inawaitThe previous code executes immediately, so async1 start is immediately printed. > metawaitWhen will beawaitThe following expression is executed once, so async2 is followed by async2awaitSo this code right here is going to beconsole.log('async1 end'Join microTaskPromiseThe async1 function is then jumped out of the queue to execute the following code3.> meetPromiseInstance. Due to thePromiseThe. Then function is executed immediately, and the subsequent. Then is distributed to microTaskPromiseGet in line. Therefore, promise1 is printed and resolve is executed to allocate promisE2 to the corresponding queue4.< p style = "max-width: 100%; clear: both; min-height: 1em; After each macro task is executed, Microtasks are checked to see if there are any; If so, execute Microtasks until the Microtask Queue is cleared. So after the script task is finished, the search starts to clear the microtask queue. At this point, in the microtask,PromiseThe queue has two tasks async1 end and promise2, so asynC1 end, promise2 is printed in sequence. The first round of the loop is complete when all Microtasks have been executed5.The second cycle starts again with the macro task queue. There is only one macro task at this pointsetTimeout, take out direct output, so the whole process endsCopy the code
Simple exercises
function fn(){ console.log(1); setTimeout(() => { console.log(2); Promise.resolve().then(() => { console.log(3); }); }, 0); new Promise((resolve, reject) => { console.log(4); resolve(5); }).then(data => { console.log(data); }); setTimeout(() => { console.log(6); }, 0); console.log(7); } fn();Copy the code
The decomposition process
- Execute function synchronization statements;
-
step1
console.log(1); Copy the code
Execute stack: [console] Macro task: [] micro task: []
Print result: 1
-
step2
SetTimeout (() => {// This callback is called callback1. SetTimeout is a macro task, so put it in the macro queue console.log(2); Promise.resolve().then(() => { console.log(3) }); });Copy the code
Execution stack: [setTimeout] Macro task: [callback1] micro task: []
Print result: 1
-
step3
New Promise((resolve, reject) => {// Note that console.log(4) is synchronized; Resolve (5)}).then((data) => {// This callback is called callback2. });Copy the code
Execution stack: [promise] Macro task: [callback1] Micro task: [callback2]
Print result: 1 4
-
step4
SetTimeout (() => {// This callback is called callback3. SetTimeout is a macro task, so put it in the macro queue console.log(6); })Copy the code
Execution stack: [setTimeout] Macro task: [Callback1, callback3] Micro task: [callback2]
Print result: 1 4
-
step5
console.log(7) Copy the code
Execute stack: [console] Macro task: [Callback1, callback3] micro task: [callback2]
Print result: 1 4 7
- The synchronization statement is executed from
The queue
, and execute the tasks untilThe queue
Is empty. (that is, the current macro task execution stack is finished, and the task of clearing the microqueue begins)
-
step6
Console. log(data) // Where data is the Promise success parameter 5Copy the code
Execution stack: [Callback2] Macro task: [Callback1, Callback3] micro task: []
Print result: 1 4 7 5
- here
The queue
There is only one task in theMacro queue
To execute the task
-
step7
console.log(2); Copy the code
Execution stack: [Callback1] Macro Task: [callback3] micro task: []
Print result: 1 4 7 5 2
However, when callback1 is executed, another Promise is encountered. After the asynchronous execution, the Promise registers a callback4 function in the microqueue
-
step8
Promise.resolve().then(() => {// This callback is called callback4. Promise is a microtask, so put it in the microqueue console.log(3); });Copy the code
Execution stack: [Promise] Macro task: [callback3] Micro task: [callback4]
Print result: 1 4 7 5 2
- After removing a macrotask and executing it, remove it from the microtask queue
-
step9
console.log(3) Copy the code
Execution stack: [Callback4] Macro Task: [callback3] micro task: []
Print result: 1 4 7 5 2 3
The queue
All done, then goMacro queue
Take the first task to execute
-
step10
console.log(3) Copy the code
Execution stack: [callBack3] Macro task: [] micro task: []
Print result: 1 4 7 5 2 3 6
-
All the above execution is completed, the execution stack, macro queue, micro queue are empty
Execution stack: [] Macro task: [] micro task: []
Print result: 1 4 7 5 2 3 6
A little bit more complicated practice problem
There are still no problems with this question, which means that you have mastered this knowledge point
function fn(){
console.log(1);
setTimeout(() = > {
console.log(2);
Promise.resolve().then(() = > {
console.log(3)}); });new Promise((resolve, reject) = > {
console.log(4)
resolve(5)
}).then((data) = > {
console.log(data);
Promise.resolve().then(() = > {
console.log(6)
}).then(() = > {
console.log(7)
setTimeout(() = > {
console.log(8)},0);
});
})
setTimeout(() = > {
console.log(9);
})
console.log(10); } fn(); The answer1
4
10
5
6
7
2
3
9
8
Copy the code