preface

The goal of this article is to thoroughly understand javascript’s event loops, so punch me if you don’t understand it after reading this.

Whether you’re a javascript novice or veteran, or a daily developer, it’s not uncommon to encounter situations where given a few lines of code, you need to know what the output is and in what order. Since javascript is a single-threaded language, we can conclude that:

Javascript executes statements in the order in which they appearCopy the code

At this point you might want to punch someone in the face: Didn’t I know that JS executes 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 browser to verify, the result is completely wrong, instant meng, said good line by line implementation?

Js event loop

Js is single-threaded, which 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? Of course not, 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. Let’s 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.

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 function success.
  • Execute console.log(‘ End of code execution ‘). Print out code execution ends
  • The Ajax Event completes, and the success callback enters the Event Queue.
  • The main thread reads and retrieves the success function from the Event Queue and executes it. Print off to send successfully

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.

Love to 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) // Suppose the execution time is much longer than 3 seconds
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 () enters the Event Table and registers the callback function to start the timer.
  • Execute the sleep function slowly, very slowly, and the timing continues.
  • After 3 seconds, task() enters the Event Queue and the timeout Event is finished.
  • Task () finally enters the main thread from the Event Queue.

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);

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

// 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.

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.

Promise

We’ve looked at traditional timers, and then we looked at Promise’s performance. The definition and function of Promise will not be described in this article. Those who do not know Promise can learn from Teacher Ruan Yifeng. 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. Then, catch, finally

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

After an event loops back, it starts to execute tasks in the task queue, but these tasks have priority. Therefore, tasks in the microtask queue are preferentially executed, and tasks in the macro task are executed after execution.

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

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

console.log('console');

Copy the code
  • This code goes to the main thread as a macro task.
  • If setTimeout is encountered first, its callback function is registered and distributed to the macro task Event Queue.
  • When a Promise is encountered, the new Promise is immediately executed, and the then function is distributed to the microtask Event Queue.
  • If console.log() is encountered, execute immediately.
  • Ok, the overall code script as the first macro task execution end, see what microtasks? We found that then is executed inside the Event Queue, the microtask.
  • 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 callback function for setTimeout in the Event Queue macro task and executed it immediately.
  • The end.

The execution result is

// promise
// console
// then
// setTimeout
Copy the code

test

Let’s examine the following two examples to see if you really understand the js implementation mechanism:

Example 1

console.log('1');
setTimeout(function () {
    console.log('2');
    new Promise(function (resolve) {
        console.log('4');
        resolve();
    }).then(function () {
        console.log('5')})},0)
new Promise(function (resolve) {
    console.log('7');
    resolve();
}).then(function () {
    console.log('8')})setTimeout(function () {
    console.log('9');
    new Promise(function (resolve) {
        console.log('11');
        resolve();
    }).then(function () {
        console.log('12')})},0)
Copy the code

The first event loop:

  1. The overall code enters the main thread as the first macro task, encounters console.log, and prints 1.
  2. When setTimeout is encountered, its callback function is distributed to the macro task Event Queue. Let’s call it setTimeout1.
  3. If a Promise is met, the new Promise is executed, and output 7. Then is distributed to the microtask Event Queue. Let’s call it then1.
  4. We’ve encountered setTimeout again, and the callback function is distributed to the macro task Event Queue, which we’ll call setTimeout2.
Macro task Event Queue Microtask Event Queue
setTimeout1 then1
setTimeout2

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 discovered the then1 microtask. Execute then1, output 8. The first round of the event loop is officially over. The result of this round is output 1,7,8.

The second event loop:

Then the second round of the time loop starts with the setTimeout1 macro task: output 2, the new Promise immediately executes output 4, and then is also distributed to the microtask Event Queue, recorded as then2.

Macro task Event Queue Microtask Event Queue
setTimeout2 then2

The second round of event loop macro task is over and we find then2 microtask to execute. The output of 5. The second round of event loop ends, and the second round outputs 2,4,5.

Third event loop: now there’s only setTimeout2 left, execute. Output 9 directly. Execute new Promise directly, output 11. Distribute the THEN to the microtask Event Queue, denoted as THEN3. The macro task of the third round of event loop is finished. The microtask then3 is executed. The output of 12. 9,11,12 is displayed in the third round of the event loop.

So the final answer is: 1, 7,8,2,4,5,9,11, 12

Example 2

  Promise.resolve().then(() = > {
     console.log('Promise1')
     setTimeout(function(){
       console.log("setTimeout1")},10)})setTimeout(() = > {
     console.log('setTimeout2')
     Promise.resolve().then(() = > {
       console.log('Promise2')})},10)
Copy the code

The first time the entire code of the Event loop enters the main thread as the first macro task and encounters a Promise, it is then distributed to the microtask Event Queue. Let’s call it then1. When setTimeout is encountered, its callback function is distributed to the macro task Event Queue. Let’s call it setTimeout1.

The tasks are:

Macro task Event Queue Microtask Event Queue
setTimeout1 then1

The first macro task completes, and the search for microtasks begins. Then1 is found, then execute then1 microtask, print out Promise1, encounter setTimeout in this microtask, then distribute its callback function to macro task Event Queue. Let’s just remember setTimeout2. Then the first event loop ends here.

The second event loop:

In this case, the task is

Macro task Event Queue Microtask Event Queue
setTimeout1
setTimeout2

The macro task setTimeout1 prints out setTimeout2, then it encounters a Promise and then distributes it to the microtask Event Queue. Let’s call that then2. SetTimeout1 macro task execution is complete, start to find then2, then2, print out the e2. The second event loop ends

Third event loop: only one setTimeout2 macro task was executed directly, printing setTimeout1.

So it’s the print order: Promise1, setTimeout2, Promise2, setTimeout1

Write in the last

  1. Javascript is a single-threaded language
  2. 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!

FAQ:

Why is JavaScript single-threaded?

One of the characteristics of the JavaScript language is that it is single threaded, that is, only one thing can be done at a time. So why can’t JavaScript have multiple threads? That will improve efficiency.

JavaScript is single threaded, depending on what it’s used for. As a browser scripting language, JavaScript’s primary use is to interact with users and manipulate the DOM. This determines that it has to be single threaded, otherwise it will cause complex synchronization issues. For example, if JavaScript has two threads at the same time, one adding content to a DOM node and the other removing the node, which thread should the browser use?

So, in order to avoid complexity, JavaScript has been single-threaded since its inception, and this has been a core feature of the language and will remain so.

Finally, I think it is helpful to you, so please give me a thumbs-up!

This time, getting to know JavaScript’s execution mechanism thoroughly deleted some of the descriptions, adding two examples and an understanding of why JS is single-threaded.