- javascript is executed in the order in which the statements appear
See here readers want to hit people: do not I know that js is executed line by line? You still need to say? Do not worry, just because js is a line by line execution, so we thought js are like this:
let a = '1';
console.log(a);
let b = '2';
console.log(b);
However actually js is like this:
setTimeout(function(){
console.log('setTimeout start')
});
new Promise(function(resolve){
console.log('for start');
for(var i = 0; i < 10000; i++){
i == 99 && resolve();
}
}).then(function(){
console.log('then function')
});
console.log('ending');
Following the idea that js executes statements in the order in which they appear, I confidently wrote down the output:
Go to chrome to verify, the result is completely wrong, instantly confused, said line by line execution?
We really need to figure out the javascript execution mechanism once and for all.
1. About javascript
javascript is a single-threaded language, in the latest HTML5 proposed Web-Worker, but javascript is single-threaded this core remains unchanged. So all the javascript version of the “multi-threaded” are simulated with a single thread, all javascript multi-threaded are paper tigers!
2. javascript event loop
Since js is single-threaded, it’s like a bank with only one window, where customers need to queue up one by one to do business, and similarly js tasks have to be executed one by one in sequence. If a task takes too long, then the next task must also wait. So the question is, if we want to browse the news, but the news contains ultra-high-definition picture loading is very slow, is our web page to be stuck until the picture is completely displayed? So the clever programmer divides the tasks into two categories:
- Synchronization of tasks
- asynchronous task
When we open a website, the rendering process of a web page is a bunch of synchronous tasks, such as the rendering of the page skeleton and page elements. And tasks like loading images and music, which take up a lot of resources and take a long time, are asynchronous tasks. There is a strict textual definition of this part, but the purpose of this article is to completely understand the execution mechanism with minimal learning costs, so we use the guide diagram to illustrate:
What the guide is trying to convey is expressed in words:
Synchronous and asynchronous tasks enter different execution “places”, synchronous into the main thread, asynchronous into the Event Table and register functions.
Event Table moves this function into the Event Queue when the specified thing completes.
When the task in the main thread is empty, it will go to Event Queue to read the corresponding function and enter the main thread to execute.
The above process will be repeated over and over again, which is often referred to as the Event Loop.
We can not help but ask, how to know the main thread execution stack is empty ah? js engine monitoring process process exists, will continue to check the main thread execution stack is empty, once empty, will go to the Event Queue to check whether there is a function waiting to be called.
After all the words, it’s more straightforward than a direct piece of code:
let data = [];
$.ajax({
url:www.javascript.com,
data:data,
success:() => {
console.log('success!');
}
})
console.log('ending');
Above is a simple ajax
request code:
ajax into Event Table and register the callback functionsuccess
.- Execute
console.log('ending')
.
The ajax event completes and the callback functionsuccess
enters the Event Queue.
The main thread reads the callback functionsuccess
from the Event Queue and executes it.
I’m sure that with the above text and code, you already have a preliminary understanding of the order of execution of js. Let’s move on to the advanced topic: setTimeout.
3. love and hate setTimeout
The famous setTimeout
Needless to say, everyone’s first impression of his asynchronous can be delayed execution, we often so realize the delay of 3 seconds execution:
setTimeout(() => {
console.log('3 seconds delay');
},3000)
Gradually setTimeout
used more places, the problem also appeared, sometimes clearly written delay 3 seconds, but the actual 5, 6 seconds to execute the function, this is what happened ah?
Let’s look at an example first:
setTimeout(() => {
task();
},3000)
console.log('console');
According to our previous conclusion, setTimeout
is asynchronous and the synchronous task console.log
should be executed first:
//console
//task()
Go ahead and verify that the result is correct! Then we modify the previous code:
setTimeout(() => {
task()
},3000)
sleep(10000000)
At first glance, actually almost well, but we put this code in chrome execution, but found that the console to execute task()
takes much more than 3 seconds, said the delay of three seconds, why now need so long ah?
At this point we need to re-understand the definition of setTimeout
. Let’s start with how the above code is executed:
task()
Enter Event Table and register, the clock starts.
Executing thesleep
function is slow, very slow, and the timing continues.
The 3 seconds are up, the timed eventtimeout
is completed, andtask()
enters the Event Queue, butsleep
is too slow, it hasn’t finished executing yet, so I have to wait.
sleep
Finally the execution is done, andtask()
has finally gone from Event Queue to the main thread for execution.
After the above process, we know that the setTimeout
function, is after a specified time, the task to be executed (in this case task()
) added to the Event Queue, and because it is a single-threaded task to be executed one by one, if the task in front of the time required is too long, then can only wait, resulting in a real delay time is much greater than 3 seconds.
We also often encounter setTimeout(fn,0)
such code, 0 seconds after the execution and what does it mean? Is it possible to execute it immediately?
The answer is no, the meaning of setTimeout(fn,0)
is to specify that a task is executed at the earliest available idle time of the main thread, meaning that you don’t have to wait for many seconds, as long as the synchronized tasks in the execution stack of the main thread are all executed, and the stack is empty, it will be executed immediately. Example:
//1
console.log('prioritize');
setTimeout(() => {
console.log('execute')
},0);
//2
console.log('prioritize');
setTimeout(() => {
console.log('execute')
},3000);
The output of code 1 is:
The output of code 2 is:
One thing to add about setTimeout
is that even if the main thread is empty, 0 milliseconds is practically unattainable. The minimum is 4 milliseconds according to the HTML standard. For those who are interested, you can find out for yourself.
4. Hated and loved setInterval
Having said that about setTimeout
, we can’t miss its twin setInterval
. They are similar, except that the latter is executed in a loop. In terms of execution order, setInterval
will place registered functions into the Event Queue at specified intervals, and if the previous task takes too long, it will also have to wait.
The only point to note is that for setInterval(fn,ms)
, we already know that instead of fn
executing every second past ms
, fn
enters the Event Queue every second past ms
. Once the callback function fn
for setInterval
executes past the delay time ms
, then no time interval is visible at all. The reader is invited to savor this statement.
5. Promise and process.nextTick(callback)
We’ve already looked at traditional timers, so let’s explore how Promise
and process.nextTick(callback)
behave.
Promise
If you don’t know the definition and function of Promise, please refer to Mr. Ruan Yifeng’s Promise. process.nextTick(callback)
is similar to the node.js version of “setTimeout”, which calls the callback function in the next loop of the event loop.
Let’s get to the point, where we have a more fine-grained definition of a task other than the broadly defined synchronous and asynchronous tasks:
macro-task (macro task): including the overall code script, setTimeout, setInterval
micro-task: Promise, process.nextTick
Different types of tasks go into the corresponding Event Queue, for example setTimeout
and setInterval
go into the same Event Queue.
The order of the event loop determines the order in which the js code is executed. After entering the overall code (macro tasks), the first loop starts. Then all microtasks are executed. Then it starts again with the macro task, finds one of the task queues to execute and then executes all the microtasks. Sounds a bit roundabout, so let’s illustrate with a piece of code from the very beginning of the article:
setTimeout(function() {
console.log('setTimeout');
})
new Promise(function(resolve) {
console.log('promise');
}).then(function() {
console.log('then');
})
console.log('console');
- This code goes into the main thread as a macro task.
EncountersetTimeout
first, then register its callback function and distribute it to the macro task Event Queue. (The registration process is the same as above and will not be described below.)
The next encounter isPromise
,new Promise
is executed immediately, and thethen
function is distributed to the microtask Event Queue.- Encounter
console.log()
and execute it immediately.
Well, the overall code script ends as the first macro task execution, see what microtasks are there? We foundthen
inside the microtask Event Queue, executing.
OK, the first event loop is over and we start the second loop, starting with the macro task Event Queue of course. We found the callback function corresponding tosetTimeout
in the Macro Task Event Queue and execute it immediately.- 结束。
The relationship between the event loop, macro task, and micro task is shown in the figure:
Let’s analyze a more complex piece of code to see if you’ve really grasped the js execution mechanism:
console.log('1');
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})
The first event loop flow is analyzed as follows:
The overall script enters the main thread as the first macro task and encountersconsole.log
, which outputs 1.
EncounteringsetTimeout
, its callback function is distributed to the macro task Event Queue. We will notesetTimeout1
for the time being.
Encounteringprocess.nextTick()
, its callback function is distributed to the microtask Event Queue. We denote this asprocess1
.
WhenPromise
is encountered,new Promise
is executed directly and outputs 7.then
is distributed to the microtask Event Queue. We denote it asthen1
.
Again, we encounteredsetTimeout
, whose callback function is distributed to the macro task Event Queue, which we noted assetTimeout2
.
Macro Task Event Queue | Microtask Event Queue |
---|---|
setTimeout1 | process1 |
setTimeout2 | then1 |
The table above shows the situation of each Event Queue at the end of the first round of event loop macro tasks, when 1 and 7 have been output.
We found two microtasksprocess1
andthen1
.Execute
process1
,output 6.Execute
then1
and output 8.
Well, the first event loop is officially over, and the results of this round are outputs 1, 7, 6, and 8. Then the second time loop starts with the setTimeout1
macro task:
Start with output 2. Next, you encounterprocess.nextTick()
, which is also distributed to the microtask Event Queue atprocess2
.new Promise
Immediately execute Output 4,then
is also distributed to the microtask Event Queue, noted asthen2
.
Macro Task Event Queue | Microtask Event Queue |
---|---|
setTimeout2 | process2 |
then2 |
At the end of the second round of event loop macrotasks, we find that there are two microtasks,process2
andthen2
, that can be executed.- Output 3.
- Output 5.
- The second event loop ends with the second output 2, 4, 3, 5.
The third event loop begins, and at this point only setTimeout2 remains to be executed.- Direct output 9.
Distributeprocess.nextTick()
to the microtask Event Queue. Noted asprocess3
.- Execute
new Promise
directly and output 11.
Distributethen
to the microtask Event Queue, noted asthen3
.
Macro Task Event Queue | Microtask Event Queue |
---|---|
process3 | |
then3 |
The third round of event loop macro task execution ends with the execution of two microtasksprocess3
andthen3
.- Output 10.
- Output 12.
The third event loop ends with the third output 9, 11, 10, 12.
For the whole code, three event loops are performed, and the complete output is 1, 7, 6, 8, 2, 4, 3, 5, 9, 11, 10, 12. (Please note that the event-listening dependency on libuv in the node environment is not exactly the same as in the front-end environment, and the order of the outputs may be inaccurate)
6. Write at the end
(1) js asynchronous
We said from the very beginning that javascript is a single-threaded language, no matter what the new framework of the new syntax sugar to achieve the so-called asynchronous, in fact, are synchronous methods to simulate, firmly grasp the single-threaded point is very important.
(2) Event Loop
The event loop is a way for js to implement asynchrony and the execution mechanism of js.
(3) javascript execution and running
There is a big difference between execution and run, javascript is executed differently in different environments, like node, browser, Ringo, and so on. Whereas running mostly refers to the javascript parsing engine, which is unified.
(4) setImmediate
There are many other kinds of microtasks and macrotasks, such as setImmediate
and so on, the execution of which all have something in common, so interested students can learn about them on their own.
(5) The end of the end
- javascript is a single-threaded language
Event Loop is a javascript execution mechanism
Firmly grasp the two basic points, to seriously learn javascript as the center, early realization of the great dream of becoming a front-end master!