•  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 function success .
  •  Execute console.log('ending') .

  • The ajax event completes and the callback function success enters the Event Queue.

  • The main thread reads the callback function success 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 the sleep function is slow, very slow, and the timing continues.

  • The 3 seconds are up, the timed event timeout is completed, and task() enters the Event Queue, but sleep is too slow, it hasn’t finished executing yet, so I have to wait.

  • sleep Finally the execution is done, and task() 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.

  • Encounter setTimeout 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 is Promise , new Promise is executed immediately, and the then 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 found then 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 to setTimeout 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 encounters console.log , which outputs 1.

  • Encountering setTimeout , its callback function is distributed to the macro task Event Queue. We will note setTimeout1 for the time being.

  • Encountering process.nextTick() , its callback function is distributed to the microtask Event Queue. We denote this as process1 .

  • When Promise is encountered, new Promise is executed directly and outputs 7. then is distributed to the microtask Event Queue. We denote it as then1 .

  • Again, we encountered setTimeout , whose callback function is distributed to the macro task Event Queue, which we noted as setTimeout2 .
 Macro Task Event Queue Microtask Event Queue
setTimeout1process1
setTimeout2then1

  • 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 microtasks process1 and then1 .

  •  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 encounter process.nextTick() , which is also distributed to the microtask Event Queue at process2 . new Promise Immediately execute Output 4, then is also distributed to the microtask Event Queue, noted as then2 .
 Macro Task Event Queue Microtask Event Queue
setTimeout2process2
then2

  • At the end of the second round of event loop macrotasks, we find that there are two microtasks, process2 and then2 , 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.

  • Distribute process.nextTick() to the microtask Event Queue. Noted as process3 .
  •  Execute new Promise directly and output 11.

  • Distribute then to the microtask Event Queue, noted as then3 .
 Macro Task Event Queue Microtask Event Queue
process3
then3

  • The third round of event loop macro task execution ends with the execution of two microtasks process3 and then3 .
  •  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!

By hbb

Leave a Reply

Your email address will not be published. Required fields are marked *