Execution mechanism of JS engine: exploring EventLoop (including Macro Task and Micro Task)

In my opinion, understanding the execution mechanism of the JS engine is crucial to understanding the JS engine. Today, I will sort out the execution mechanism of the JS engine.

First explain the nouns in the title: (You will have an understanding of these concepts after reading this article)

Event Loop: Event Loop
Micro Task: Micro Task
Macro Task: Macro Task

Before reading this article, we need to know two important points.

(1) JS is a single-threaded language

(2) The Event Loop of JS is the execution mechanism of JS. In-depth understanding of JS execution is equivalent to in-depth understanding of event loop in JS

 

1. Why is JS a single-threaded language?

javascript is a single-threaded language, and Web-Worker was proposed in the latest HTML5, but the core of javascript being single-threaded has not changed. related to its use. As a browser scripting language, JavaScript's primary use is to interact with the user, and to manipulate the DOM. This determines that it can only be a single thread, otherwise it will bring complex synchronization problems. For example, assuming that JavaScript has two threads at the same time, one thread adds content to a DOM node, and the other thread deletes this node, which thread should the browser take?

So, to avoid complexity, JavaScript has been single-threaded since its inception, which has been a core feature of the language and will not change in the future.

 

2. What is a task queue?

2.1 Synchronous tasks and asynchronous tasks

Single thread means that all tasks need to be queued. All tasks can be divided into two types, one is synchronous task (synchronous), the other is asynchronous task (asynchronous). Synchronous tasks refer to tasks queued for execution on the main thread, and the latter task can only be executed after the previous task is executed; asynchronous tasks refer to tasks that do not enter the main thread but enter the "task queue" (task queue) Task, only the "task queue" notifies the main thread that an asynchronous task can be executed, the task will enter the main thread for execution. The operation mechanism of asynchronous execution is as follows:

(1) All synchronization tasks are executed on the main thread, forming an execution context stack.

(2) In addition to the main thread, there is also a "task queue" (task queue). As soon as the asynchronous task has a running result, an event is placed in the "task queue".

(3) Once all the synchronization tasks in the "execution stack" are executed, the system will read the "task queue" to see what events are in it. Those corresponding asynchronous tasks then end the waiting state, enter the execution stack, and start execution.

(4) The main thread keeps repeating the third step above.

 

2.2 JS engine execution model

From a macro point of view, the execution of js is single-threaded. All asynchronous results are scheduled and scheduled through the "Task Queue". The message queue stores the tasks one by one. The specification stipulates , Task is divided into two categories, namely Macro Task (macro task) and Micro Task (micro task), and after each Macro Task ends, all Micro Tasks must be emptied.

From a macro perspective, Macrotask will enter the Macro Task Queue, and Microtask will enter the Micro Task Queue. The Micro Task is divided into two queues. The 'Micro Task Queue' Promisestores microtasks. And the 'Tick Task Queue' is dedicated to storing process.nextTicktasks. Now let's take a look at the classification of how to do it.

  • Macrotask includes:
    • setImmediate
    • setTimeout
    • setInterval
  • Microtask includes:
    • process.nextTick
    • Promise
    • Object.observe
    • MutaionObserver

Said, ' After each Macro Task ends, all Micro Tasks must be emptied '. The engine will traverse the Macro Task Queue, and after each Macro Task is completed, it will traverse all the tasks in the Tick Task Queue, and then Traverse all tasks in the Micro Task Queue. ( nextTickWill be better than PromiseExecute )

 

3. Event Loop

The main thread reads events from the "task queue", and this process is cyclical, so the entire operating mechanism is also called Event Loop.

The code execution flow charts in the three task queues are as follows:

Let's check it out with an example:

console.log('main1');

process.nextTick(function() {
    console.log('process.nextTick1');
});

setTimeout(function() {
    console.log('setTimeout');
    process.nextTick(function() {
        console.log('process.nextTick2');
    });
}, 0);

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

console.log('main2');

analyse as below

  1. Start executing the code, output main1, process.nextTick is put into tickTaskQueen, setTimeout is put into macroTaskQueen, new Promise is executed and output promise, then method is put into MicroTaskQueen, and then the last line of code console.log outputs main2
  2. After the current macro task is executed, start to clear the micro task, first clear tickTaskQueen, execute console.log('process.nextTick1'); output 'process.nextTick1; then clear MicroTaskQueen and execute console.log('promise then'); output promise then; all microtasks are cleared.
  3. Start the next eventLoop; execute setTimeout; the first line console.log('setTimeout'); output setTimeout; process.nextTick puts the task into tickTaskQueen; the current macro task is executed; start to clear MicroTaskQueen, clear tickTaskQueen, and execute console.log ('process.nextTick2'); output process.nextTick2;

 

4. Event Loop in Node.js

Node.js is also a single-threaded Event Loop, but its operating mechanism is different from the browser environment.

According to the above figure, the operating mechanism of Node.js is as follows.

(1) The V8 engine parses the JavaScript script.

(2) The parsed code calls the Node API.

(3) The libuv library is responsible for the execution of the Node API. It assigns different tasks to different threads to form an Event Loop (event loop), and returns the execution result of the task to the V8 engine in an asynchronous manner.

(4) The V8 engine returns the result to the user.

Node.js also provides two other methods related to "task queue": process.nextTick and setImmediate . They can help us deepen our understanding of "task queues".

process.nextTick(function A() {
  console.log(1);
  process.nextTick(function B(){console.log(2);});
});

setTimeout(function timeout() {
  console.log('TIMEOUT FIRED');
}, 0)
// 1
// 2
// TIMEOUT FIRED

In the above code, because the callback function specified by the process.nextTick method is always triggered at the end of the current "execution stack", not only function A is executed before the callback function timeout specified by setTimeout, but also function B is executed before timeout. This means that if there are multiple process.nextTick statements (whether they are nested or not), they will all be executed on the current "execution stack".

 

setImmediate(function (){
  setImmediate(function A() {
    console.log(1);
    setImmediate(function B(){console.log(2);});
  });

  setTimeout(function timeout() {
    console.log('TIMEOUT FIRED');
  }, 0);
});
// 1
// TIMEOUT FIRED
// 2

In the above code, setImmediate and setTimeout are encapsulated in a setImmediate, and its running result is always 1--TIMEOUT FIRED--2, then function A must be triggered before timeout. As for the second row after TIMEOUT FIRED (that is, function B is triggered after timeout), it is because setImmediate always registers the event to the next round of Event Loop, so function A and timeout are executed in the same round of Loop, and function B is executed in the next round of Loop. Round Loop execution.

From this, we get an important difference between process.nextTick and setImmediate: multiple process.nextTick statements are always executed once in the current "execution stack", and multiple setImmediate may need multiple loops to execute.

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324834387&siteId=291194637