preface

This article will give you to share is JS great eventloop mechanism (Event loop), through intensive reading can understand the browser eventLoop and NodeJS Eventloop in their characteristics and the difference between the two, this is also the author column native JS soul interrogating the third, the follow-up will continue to update, Welcome to follow! (Long, there are expressed wrong welcome to comment section to point out and share.)

What is an EventLoop? Why bother looking at it?

As we all know, JS is a single-threaded scripting language, and Event Loop is a JavaScript single-threaded runtime mechanism for browsers or NodeJS to process various events without causing blocking, which is also the principle of using asynchronous. So how does the JS engine do a single thread processing synchronization, asynchronous, timer all kinds of events without causing blocking? That’s why we’re looking at Event Loops.

Second, basic knowledge reserve

Processes and Threads

Official: A process is the smallest unit of CPU resource allocation and describes how long it takes the CPU to run instructions and load and save the context. A thread is the minimum unit of CPU scheduling and describes the time required to execute a single instruction. (A process can have multiple threads, so threads are smaller units of the process.)

For the operating system, thread is the smallest unit of execution, and process is the smallest unit of resource management.

Here’s an example to help you understand: In Chrome, for example, when a new TAB page opens, you create a process. In this process, there will be multiple threads: rendering thread, JS engine thread, HTTP request thread and so on.

Multi-process and multi-threaded

  • 1, multi-process: at the same time, the same computer system if two or more processes are allowed to be in the running state. The benefits of multiprocessing are obvious: the ability to work with multiple processes simultaneously, without interfering with each other, can significantly improve engine performance.
  • 2, multithreading: the program contains multiple execution flow, that is, in a program can simultaneously run multiple different threads to perform different tasks, that is, allow the simultaneous execution of multiple lines of JS code.

Here’s a question worth thinking about: Why not change JS to multithreading? What are the benefits of JS single-threading?

  • First, the effect of JS single threading:

The JS engine blocks UI rendering while the JS engine is running.

  • JS is not modified for multithreaded reasons

Anyone familiar with JS knows that JS code can modify the DOM structure. If JS is changed to multithreading, it will result in the UI rendering thread working while the JS engine is running, which may lead to unsafe rendering of the UI.

  • Benefits of JS single threading
  1. Save running memory
  2. Saves context switching time

Micro-tasks and Macro-Tasks

The author has deliberately checked the concepts of Microtask and Macrotask, but did not find relevant descriptions in the official documents, therefore, the definition of the two is not known, the following can only understand the two concepts through examples. Please let us know in the comments section when you find it.

Using the example of a bank handling business, you can compare the JS engine to a bank clerk. We usually go to the bank to deal with business, first of all, we need to queue to get the number, which is the task queue of the engine processing thread. The person corresponding to each number can be regarded as a macro task. When the person corresponding to the number is called, that is, the engine starts processing a macro task in the queue; And each person in the window may need to deal with multiple business, these business is actually the micro-task queue, the salesman only these micro-tasks are completely processed, can continue to call, to deal with the next macro task. (This is mainly to visualize the concept of microtasks and macro tasks. Both have one thing in common: they are tasks that need to be executed asynchronously.)

  • What are the macro tasks in JS? What are microtasks?
  1. Micro – Task: process. NextTick, promise, promise. Then, MutationObserver
  2. Macro – Task: script, setTimeout and setInterval, setImmediate, I/O, UI rendering
  • There are also two mechanisms for asynchronous callbacks in JS that need to be understood
  1. Encapsulate the asynchronous callback function as a macro task, add it to the end of the message queue, and execute the callback function when the loop system executes the task.
  2. The execution time is to execute the callback function after the main function has finished, but before the current macro task has finished, usually in the form of a microtask
  • A microtask is a function that needs to be executed asynchronously, after the main function completes but before the current macro task finishes

EventLoop in the browser

The event loop mechanism in the browser works in several steps:

  • Message addition: In the browser, every time an event listener is bound to the event, a message is added to the message queue.

  • Message processing: A JS runtime contains a message queue of messages to be processed, and each message is associated with a callback function to process the message. The runtime processes the messages in the queue starting with the first messages to enter the queue. The processed message is removed from the queue and the associated function is called as an input parameter. Calling the function creates a new stack frame, pushes it onto the call stack, and the function is processed until the execution stack is empty. The event loop then proceeds to process the next message in the queue.

2. Here’s an example:

console.log('script start');

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

Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});
console.log('script end');
Copy the code

First execution

Execute the synchronization code to add macro and micro events to the corresponding queues. Here print script start -> script end, macro task queue: setTimeout. Microtask queues: promise.then1, promise.then2. After the script is executed, the execution stack is empty, and the microtask queue is executed. The items in the microtask queue are put into the execution stack in order for execution until the execution stack is empty again, and the execution ends.

Script start -> script end ->promise1 -> promise2

Second execution

Take the setTimeout off the macro task queue and execute it on the stack. After the execution, the stack is empty, go to the microtask queue to find, the microtask queue is empty, the execution is finished.

So the final print order is: script start -> script end ->promise1 -> promise2 -> setTimeout

A picture is worth a thousand words

(Note: dynamic graph fromThe post,”, this big guy is so great, this picture is very clear!

4. The characteristics, advantages and disadvantages of this event cycle mechanism

  • Feature: Only after one message has been executed in its entirety can other messages be executed.
  • Benefit: When a function is executed, it is not preempted, and only after it is finished will any other code be run to modify the data that the function operates on.
  • Cons and Fixes: When a message takes too long to process, the Web application is unable to handle user interactions, such as clicking or scrolling. To alleviate this problem, browsers typically display a “this script is taking too long to run” dialog box. It is a good practice to shorten the processing time of a single message and to crop a message into multiple messages where possible.

5, summary

  • The order of execution of an EventLoop in the browser:
    1. The synchronization code (script file) is executed first, which is also a macro task
    2. When all synchronous code is executed, the execution stack is empty and the execution engine will query whether there is any asynchronous code to execute. If so, perform all microtasks.
    3. After all the microtasks have been performed, the execution stack is empty again, and rendering of the page can occur at this step if necessary. The first execution is complete.
    4. At the start of the second execution, the execution engine queries whether any macro tasks need to be executed. If yes, execute a macro task, that is, after the stack is empty again, query whether any microtasks need to be executed. If yes, execute all the microtasks. If no, the second execution is complete.
    5. Start the next event-loop and do the same. This is the Event loop mechanism in the browser.

EventLoop in NodeJS

Official definition of event loops in Node

Event loops are the mechanism by which Node.js handles non-blocking I/O operations — although JavaScript is single-threaded — and they move operations to the system kernel when possible. Even though most current kernels are multithreaded, they can handle multiple operations in the background. When one of these operations is complete, the kernel tells Node.js to add the appropriate callback functions to the polling queue and wait for them to execute.

Event loop on Node: Node.js uses V8 as the JS parsing engine, and I/O processing uses libuv designed by itself. Libuv is an event-driven cross-platform abstraction layer, which encloses some underlying features of different operating systems and provides a unified API.

2. Event loop resolution in Node

When node.js starts, it initializes the event loop and processes the input scripts provided. It might call some asynchronous API, schedule timer, or call process.nexttick () and then start processing the event loop. The diagram below shows a simplified model of the sequence of operations in the event loop (each box is called a phase of the event loop mechanism). As you can see, Node’s event loop is divided into six phases, which run repeatedly in sequence.Overview of each stage:

  1. Enter data from the outside and go to poll: Get a new I/O event, where the node blocks when appropriate
  2. Enter the Check phase: Perform a callback to setImmediate
  3. Enter the close callback phase: perform the close event callback of the socket
  4. Enter the Timers phase: This phase executes the callback of timer(setTimeout,setInterval)
  5. Enter the I/O event callback phase (I/Ocallbacks) : Handle with a few unexecuted I/Ocallbacks from the previous loop
  6. Enter the idle phase (idle,prepare) : Used only in a node
  7. After the round is over, the next round is entered

3. The stages that are important to understanding the order of node’s event loop are described in detail below:

(1) the timers

  • In this phase, the callback execution of the timer is performed.
  • The timer (setTimeout,setInterval) takes two arguments. The first argument is its callback function, and the second argument specifies the threshold at which the provided callback can be executed, rather than the exact time the user wants it to be executed. That is, after a specified interval of time, the callback to the timer is executed as early as possible. However, system scheduling or other running callbacks may cause the timer’s callback to execute beyond the specified time.

(2) the poll

  • Two functions of this phase:
  1. Calculate the time that should block and poll
  2. Processes events in the polling queue

(3) the check

  • This phase allows people to perform callbacks immediately after the polling phase is complete. If the poll phase goes idle, and the script is queued with setImmediate(), the event loop may continue to the check phase instead of waiting.

  • SetImmediate () is actually a special timer that runs in a separate phase of the event loop. It uses a Libuv API to schedule callbacks to be executed after the polling phase is complete.

  • Typically, as the code executes, the event loop eventually hits the polling phase, where it waits for incoming connections, requests, and so on. However, if the callback has been scheduled using setImmediate(), and the polling phase goes idle, it ends the phase and proceeds to the checking phase instead of continuing to wait for the polling event.

Note: Each phase has its own characteristics, but the same is that every time the event loop enters a specified phase, any operation specific to that phase is performed, and then the callbacks in that queue are executed until the queue runs out or the maximum number of callbacks has been executed. The event loop moves to the next stage only when the queue is exhausted or the callback limit is reached.

4, For example

After reading so many relatively official parsing, if you are a beginner, you may have a bit of confusion, so back to the original question: what is the order of the event loop in Node? Here is an example to answer this question.

setTimeout(() = >{
    console.log('timer1')
    Promise.resolve().then(function() {
        console.log('promise1')})},0)
setTimeout(() = >{
    console.log('timer2')
    Promise.resolve().then(function() {
        console.log('promise2')})},0)
Copy the code

We need to use two versions of Node to describe the order and results:

(1) Node11 version

  • When Node updates to this version, you can actually say that Node’s event loop is similar to that of the browser. In this version, the microtask queue is executed immediately after executing a macro task in a phase, so it is consistent with the result of running on the browser side. The final result istimer1 -> promise1 -> timer2 -> promise2
  • This version runs in the following order:
    1. First execute the synchronization code (script file), put the two timers into the timer queue in turn, synchronization code execution is finished, the call stack is idle, start to execute the task queue.
    2. Enter the Timers phase, run the callback function of Timer1, print timer1, and put the peromise.then callback into the microtask queue. After timer1 is executed, the call stack is empty again, execute the microtask queue immediately, and print promisE1
    3. Repeat operation 2 by executing the callback of Timer2
    4. After the timers phase finishes, the event loop mechanism enters the next phase

(2) Node10 and earlier

  • Prior to Node11, the execution sequence of the event loop was quite different from that of the browser. The main reason is that the node executes the microtask queue after each of the six phases, so the execution result is as follows:timer1 -> timer2 -> promise1 -> promise2

  • The running order in this version is:
    1. First execute the synchronization code (script file), put the two timers into the timer queue in turn, synchronization code execution is finished, the call stack is idle, start to execute the task queue.
    2. Enter the Timers phase, execute the callbacks of the two timers successively, and print timer1 and timer2 successively. Then and then put promise1.then and promise2.then into the microtask queue in turn, at which time the timers end
    3. Execute the microtask queue and print PromisE1 and Promise2 successively
    4. After the execution of the microtask queue, the event loop moves to the next stage

What is the difference between browser EventLoop and NodeJS EventLoop?

  • After Node11, the node-side and browser-side event loops are very close, but if you want to distinguish between them, I understand that the definition of each loop is different:
    • On the browser side, a round is completed by executing a macro task and then executing the microtasks in the queue of microtasks until all of them have been executed. The next loop starts with the execution of the next macro task.
    • For Node11, Node’s Libuv engine (as mentioned in Node’s official definition of event loops) divides the event loop into six phases. In each of these phases, macro tasks are executed. The microtask queue is executed immediately after each macro task is executed (which is the same as on the browsing side). Macro tasks continue to be executed, and then microtasks. When the execution of macro tasks in this phase is complete, the next phase is entered. All of this is in the same loop, and the loop is not complete until all six stages of the event loop defined by Node’s Libuv engine have been completed.

Six, the last

  • In this article, there are some knowledge that the author does not really understand, such as:
    1. Compare process.nexttick () to setImmediate()
    2. Compare setImmediate() to setTimeout()
    3. Why use process.nexttick ()

    After I have my own understanding, I will continue to update this article. In addition, I have been doing something recently: I summarize my learning into articles, and then build my own knowledge system through the articles. This article has been included in my articleNative JS soul asksThe column will continue to be updated in the future. Interested partners can continue to pay attention to it, and learn and make progress together with the author. In addition, if you have any suggestions for this article or my presentation, you are welcome to comment on it!!

Refer to the article

  • What is the difference between browser and Node Event loops?
  • One last time I figured out the Event Loop
  • Official document for node.js event loop
  • Understanding the js event loop (Node.js)