The goal of this article is to make sure that you understand the mechanics of javascript execution thoroughly, so punch me if you don’t.

Whether you’re new to javascript or a veteran, whether you’re interviewing for a job, or working as a developer on a daily basis, you’ll often find yourself in a situation where given a few lines of code, you need to know what to output and in what order. Since javascript is a single-threaded language, we can conclude that:

  • Javascript executes statements in the order in which they appear

Did I not know that JS is executed line by line? You tell me? Hold on a second, because js is executed line by line, so we assume that js is all like this:

let a = '1';
console.log(a);

let b = '2';
console.log(b);
Copy the code

However, js actually looks like this:

setTimeout(function(){
    console.log('The timer is on')});new Promise(function(resolve){
    console.log('I'm about to execute the for loop.');
    for(var i = 0; i < 10000; i++){
        i == 99 && resolve();
    }
}).then(function(){
    console.log('Then function is executed')});console.log('End of code execution');
Copy the code

Given the idea that JS executes the statements in the order they appear, I confidently write the output:

//" The timer is on"
//" Let's execute the for loop now."
//" execute the then function"
//" End of code execution"
Copy the code

Go to Chrome to verify, the results are completely wrong, instant meng, said good line by line implementation?

We really have to figure out how javascript executes once and for all.

1. About the javascript

Javascript is a single-threaded language, and the web-worker has been introduced in the latest HTML5, but the core of javascript being single-threaded has not changed. So all javascript versions of “multithreading” are modeled with single threading, and all javascript multithreading is a paper tiger!

2. Javascript event loops

Since JS is single-threaded, it is like a bank with only one window, customers need to queue one by one for business, and similarly JS tasks need to be executed one by one. If one task takes too long, then the next must wait. So here’s the question: if we want to browse the news, but the ultra hd images that the news contains load slowly, do we want to be stuck until the images are fully displayed? So smart programmers divide tasks into two categories:

  • Synchronization task
  • Asynchronous tasks

When we open a website, the rendering process is a bunch of synchronization tasks, such as rendering the skeleton of the page and the elements of the page. Tasks that take up a lot of resources, such as loading pictures and music, are asynchronous. There is a strict literal definition of this part, but the purpose of this paper is to thoroughly understand the implementation mechanism with the minimum learning cost, so we use a map to illustrate:

If the content to be expressed by the map is expressed in words:

  • Synchronous and asynchronous tasks go to different “venues”, synchronously to the main thread and asynchronously to the Event Table and register functions.
  • When the specified thing is done, the Event Table moves the function to the Event Queue.
  • If the task in the main thread is empty after execution, it will go to the Event Queue to read the corresponding function and enter the main thread for execution.
  • This process is repeated over and over again, known as an Event Loop.

So how do we know that the main thread execution stack is empty? The JS engine has a monitoring process that continuously checks to see if the main thread execution stack is empty. Once empty, it checks the Event Queue to see if any functions are waiting to be called.

With so many words said, it is better to directly a piece of code is more straightforward:

let data = [];
$.ajax({
    url:www.javascript.com,
    data:data,
    success:() = > {
        console.log('Sent successfully! '); }})console.log('End of code execution');
Copy the code

Above is a simple ajax request code:

  • Ajax enters the Event Table and registers the callback functionsuccess.
  • performConsole. log(' End of code execution ').
  • Ajax event completed, callback functionsuccessEnter the Event Queue.
  • The main thread reads and retrieves the callback from the Event QueuesuccessAnd executed.

I believe that through the above text and code, you have a preliminary understanding of the js execution order. Now let’s look at the more advanced topic: setTimeout.

3. Love-hate setTimeout

The famous setTimeout goes without saying, but the first impression is that asynchron can be delayed, and we often do this by 3 seconds:

setTimeout(() = > {
    console.log('Delay 3 seconds');
},3000)
Copy the code

Sometimes it takes 5 or 6 seconds to execute a function when it is written for 3 seconds.

Let’s start with an example:

setTimeout(() = > {
    task();
},3000)
console.log('execution of the console);
Copy the code

We concluded that setTimeout is asynchronous and that console.log should be executed first.

/ / the console
//task()
Copy the code

Go check it out, the result is correct! Then let’s change the previous code:

setTimeout(() = > {
    task()
},3000)

sleep(10000000)
Copy the code

At first glance, it looks like this, but when we run this code in Chrome, it turns out that the console takes much longer than 3 seconds to execute task().

This is where we need to re-understand the setTimeout definition. Let’s start with how the above code executes:

  • task()Enter the Event Table and register to start the timer.
  • performsleepFunction, slow, very slow, the timing continues.
  • Three seconds. Time the eventtimeoutComplete,task()Enter the Event Queue, butsleepIt’s too slow. It’s not done yet. We have to wait.
  • sleepAt last the execution was completed,task()Finally from the Event Queue into the main thread execution.

We know that the setTimeout function adds the task(in this case, task()) to the Event Queue after a specified period of time. If the previous task takes too long, we can only wait. The real delay is much longer than 3 seconds.

We often run into code like setTimeout(fn,0), but what does it mean to execute after 0 seconds? Can it be executed immediately?

The answer is no. SetTimeout (fn,0) specifies the earliest available idle time for a task to be executed on the main thread. This means that the task will be executed as soon as all synchronization tasks in the main thread execution stack are completed and the stack is empty. For example:

/ / code 1
console.log('Do it here first');
setTimeout(() = > {
    console.log('It's done.')},0);
Copy the code
/ / code 2
console.log('Do it here first');
setTimeout(() = > {
    console.log('It's done.')},3000);  
Copy the code

The output of code 1 is:

// Execute here first
/ / implementation
Copy the code

The output of code 2 is:

// Execute here first
// ... 3s later
/ / implementation
Copy the code

One thing to add about setTimeout is that even if the main thread is empty, 0 milliseconds is actually unreachable. By HTML standards, the minimum is 4 milliseconds. If you are interested, you can find out.

4. Hate and love setInterval

The above said setTimeout, of course, can’t miss it twin brother setInterval. They are similar, but the latter is the execution of a loop. For order of execution, setInterval will place the registered functions in the Event Queue at specified intervals. If the previous task is too long, it will also have to wait.

The only thing to note is that, for setInterval(fn,ms), we already know that instead of fn being performed every ms second, there will be fn in the Event Queue every ms second. Once the callback to setInterval (fn) runs beyond the delay of ms, the interval is completely invisible. This sentence invites the reader to savor.

5. Promise and process. NextTick (the callback)

We’ve looked at traditional timers, and then we looked at the performance of Promises and process.Nexttick (Callback).

The definition and function of Promise will not be described in this article. Readers who do not know Promise can learn from Teacher Ruan Yifeng. 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.

Moving on, in addition to the broad definitions of synchronous and asynchronous tasks, we have a more refined definition of tasks:

  • Macro-task: includes the entire code script, setTimeout, setInterval
  • Micro-task: Promise, process. NextTick

Different types of tasks will enter the corresponding Event Queue, for example, setTimeout and setInterval will enter the same Event Queue.

The sequence of event loops determines the execution order of the JS code. After entering the overall code (macro task), start the first loop. Then perform all the microtasks. Then start from the macro task again, find one task queue to execute, then execute all microtasks. If this sounds a bit convoluted, let’s use the code at the beginning of this article to explain:

setTimeout(function() {
    console.log('setTimeout');
})

new Promise(function(resolve) {
    console.log('promise');
}).then(function() {
    console.log('then');
})

console.log('console');
Copy the code
  • This code goes to the main thread as a macro task.
  • To meet asetTimeout, its callback function is registered and distributed to the macro task Event Queue. (The registration process is the same as above and will not be described below)
  • And then I came acrossPromise.new PromiseImmediately,thenFunction to the microtask Event Queue.
  • encounterconsole.log(), immediately.
  • Ok, the overall code script as the first macro task execution end, see what microtasks? We found outthenIn the microtask Event Queue, execute.
  • Ok, the first round of the Event loop is over. Let’s start the second round of the loop, starting with the macro task Event Queue. We found the macro task Event QueuesetTimeoutThe corresponding callback function is executed immediately.
  • The end.

The relationship between event loop, macro task and micro task is shown in the figure:

Let’s look at a more complex piece of code to see if you really understand the js implementation 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')})})Copy the code

The first round of event cycle process is analyzed as follows:

  • The whole script enters the main thread as the first macro task, encounteredconsole.log, output 1.
  • encountersetTimeout, whose callback function is distributed to the macro task Event Queue. Let’s call thatsetTimeout1.
  • encounterprocess.nextTick(), whose callback function is distributed to the microtask Event Queue. We remember toprocess1.
  • encounterPromise.new PromiseExecute directly, output 7.thenIs distributed to the microtask Event Queue. We remember tothen1.
  • Meet againsetTimeout, whose callback function is distributed to the macro task Event Queue, which is denoted assetTimeout2.
Macro task Event Queue Microtask Event Queue
setTimeout1 process1
setTimeout2 then1
  • The above table shows the Event queues at the end of the macro task of the first round of Event loop, at which point 1 and 7 have been output.

  • We found two microtasks, process1 and then1.

  • Execute process1 and output 6.

  • Execute then1, output 8.

Ok, the first round of the event loop is officially over, and the result of this round is output 1,7,6,8. So the second round of the time loop starts with the setTimeout1 macro task:

  • First, output 2. And then I came acrossprocess.nextTick(), and distribute it to the micro-task Event Queue, denoted asprocess2.new PromiseImmediately execute output 4,thenAlso distributed to the microtask Event Queue, denoted asthen2.
Macro task Event Queue Microtask Event Queue
setTimeout2 process2
then2
  • At the end of the second round of event loop macros, we found that there wasprocess2andthen2Two microtasks can be performed.
  • The output of 3.
  • The output of 5.
  • The second round of event loop ends, and the second round outputs 2,4,3,5.
  • The third round of the event loop begins. At this point, only setTimeout2 is left. Execute.
  • Output 9 directly.
  • willprocess.nextTick()To the microtask Event Queue. Remember toprocess3.
  • Direct executionnew Promise, output 11.
  • willthenTo the microtask Event Queue, denoted asthen3.
Macro task Event Queue Microtask Event Queue
process3
then3
  • The third round of event loop macro task execution is completed, and two microtasks are executedprocess3andthen3.
  • The output of 10.
  • The output of 12.
  • 9,11,10,12 is displayed in the third round of the event loop.

The whole code, three event loops, the complete output is 1,7,6,8,2,4,3,5,9,11,10,12. (Note that event listening in the Node environment depends on Libuv, and the output sequence may be incorrect)

6. Write at the end

(1) asynchronous js

We’ve said from the very beginning that javascript is a single-threaded language, and whatever new frameworks and syntactic sugar are supposed to do with asynchronism is actually done in a synchronous way. It’s important to get a grip on that.

(2) Event Loop

Event loop is a method of js to realize asynchrony, and it is also the execution mechanism of JS.

(3) Execution and running of javascript

There is a big difference between execution and running. Javascript executes differently in different environments, such as Node, browser, Ringo, etc. And running mostly refers to the javascript parsing engine, which is unified.

(4)setImmediate

There are many kinds of microtasks and macro tasks, such as setImmediate and so on. The execution of all of them has something in common, and students who are interested can understand it by themselves.

(5) At last

  • Javascript is a single-threaded language
  • An Event Loop is an execution mechanism for javascript

Firmly grasp the two basic points, to seriously learn javascript as the center, as soon as possible to become a front-end master of the great dream!