Visual interpretation of js event loop and the difference between node event loop and browser event loop

technical background

The javaScript event loop (Event Loop) is the core foundation of its single-threaded language that can achieve efficient asynchronous operation. If you want to have a deeper understanding of js, you must have a clear understanding of the event loop. After the emergence of node, the operating environment of js is no longer a single browser. Similarly, there is an event loop in node. So what exactly is an event loop? There are many articles about event loops, but it seems a little difficult for some beginners to understand. This article tries to have a superficial to macro understanding of event loops, hoping to express the event loops more visually. First a few background questions.

A few background questions:

1. What is thread, process, what is the difference and connection between the two,

A process is the smallest unit of computer system resource allocation. The smallest unit has independent memory from each other, and a process has an independent memory space;

A thread is the smallest unit of computer cpu scheduling and allocation. The smallest scheduling unit is the smallest basic unit that can run independently in the cpu. A thread does not have its own memory space;

A process can have many threads, and each thread performs different tasks in parallel.

Visual understanding:

Think of Computers as a Company

A process is an independent department, and each department has its own resources;

A thread is an employee of each department. Each employee has no resources and is the smallest working unit, sharing departmental resources;

2. Why js is single-threaded

This is mainly related to the use of js. Before the emergence of node, js was used as the scripting language of the browser, mainly to realize the interaction between the user and the browser, and to operate the dom; this determines that it can only be single-threaded, otherwise it will bring Very complicated synchronization problem.

For example: if js is designed with multi-threading, if one thread wants to modify a dom element, and another thread wants to delete the dom element, the browser will be at a loss and at a loss...

Single thread and event loop (event loop)

JavaScript is single-threaded and can only be executed sequentially when executing code. In order to solve the blocking of code execution, js is asynchronous. For example, when encountering setTimeout, it will not execute after the timer content is executed. Code, but execute the code first, and then execute the timer after the time is up.

Based on this asynchronous mechanism, javaScript has its own set of rules for executing code to ensure that the code can run efficiently and without blocking. This rule is the event loop.

Both node and browser provide js with a running environment, but the operating mechanism of the two is slightly different.

Browser js operating mechanism

Different browsers have different js engines

Although browsers are different, the internal event loop rules are consistent.

node.js operating mechanism

Node.js uses v8 as the parsing engine of js, and uses libuv in I/O processing.

The libuv library is responsible for the execution of the node api, and it assigns different tasks to different threads to form an Event Loop. Return the execution result to the V8 engine in an asynchronous manner.

Event Loop in the browser

It is more brain-intensive to describe Event Loop directly in words. First, let’s introduce the related concepts of Event Loop.

Event Loop includes execution stack, event queue, microtask, macrotask, execution stack and event queue are addresses for storing events in the event loop, and microtasks and macrotasks are events executed in the event loop.

Execution stack:

After the overall code of js is loaded, it will start running. At this time, an execution context (context) will be generated. After the code is executed, the execution context will be released. For an execution context, it can also be called the current js execution environment , including the private scope, variables in the current scope, the upper scope, and the current scope object this .

Since js is single-threaded, when the previous code is executed, the following code is waiting to be executed. At this time, this part of the code (function or directly executable code) is placed in a stack, called the execution stack .

Event queue:

The above js operation only considers synchronous events. When asynchronous events (or timing events) are encountered during js execution , the corresponding events will be suspended, and js will add this event to another queue different from the current execution stack. Continue to execute the synchronous code in the current execution context. This queue that stores asynchronous events is called the event queue.

After the execution stack in an execution environment is cleared, js will check whether the event queue is empty at this time, and if it is not empty, continue to execute these events.

When executing the events in the event queue, it will follow the rules of the execution stack. First, an execution context corresponding to the current event will be generated, and then the execution stack and event queue will be generated. When the code in the execution environment is executed and the result is returned, js will Exit the execution environment and destroy the execution environment, and return to the execution environment of the previous method. Then execute the next event in the event queue, the same rules. When the event queue is emptied, the outer execution environment is destroyed and the execution ends.

It can be seen from the above analysis that the event loop refers to the execution of code in the event queue that repeats the rules of the outer execution stack, and goes deeper layer by layer to form a cycle.

Two points of attention:

1. Synchronous tasks take precedence over asynchronous tasks in the same execution context

2. The execution order of asynchronous tasks in different execution environments depends on the order in which they are added to the event queue

3. The event queue is the same in different execution environments

example:

function a(){
    console.log('a')
    setTimeout( () => {
        console.log('a1')
    },0)
}


function b(){
    console.log('b')
    setTimeout( () => {
        console.log('b1')
    },0)
}


setTimeout( () => {
    console.log('上层')
},0)

a()
b()

// 运行结果
// a
// b

// 上层
// a1
// b1

change time

function a(){
    console.log('a')
    setTimeout( () => {
        console.log('a1')
    },100)
}


function b(){
    console.log('b')
    setTimeout( () => {
        console.log('b1')
    },0)
}


setTimeout( () => {
    console.log('上层')
},10)

a()
b()

// 运行结果
// a
// b
// b1
// 上层
// a1

Visual understanding of Event Loop:

Think of the method in the js code as a patient in the hospital ,

The execution stack is all patients, in order

Synchronous tasks are direct patient visits

The asynchronous task is the patient who needs to be tested and see a doctor after getting the result

for loop

If the patients who need to be tested belong to a group, the patients waiting in the queue at the back will go to the next group after all the patients who are directly seeing the doctor from this group have finished reading.

 

Microtasks:

  • new Promise()
  • new MutaionObserver()

Macro task:

  • setInterval()
  • setTimeout()
  • overall code
  • I/O operations, UI rendering

In an event loop, asynchronous events return results and are placed in a task queue. However, depending on the type of the asynchronous event, the event will actually be queued to the corresponding macrotask queue or microtask queue. And when the current execution stack is empty, the main thread will check whether there are events in the microtask queue. If it does not exist, then go to the macro task queue to take out an event and add the corresponding return to the current execution stack; if it exists, the callback corresponding to the event in the queue will be executed in turn until the micro task queue is empty, and then go to the macro task Take out the first event from the queue, and add the corresponding callback to the current execution stack...and so on, and enter the loop.

Summary: The event queue is divided into microtask queue and macrotask queue. In the same event cycle, microtasks are always executed before macrotasks.

Visual understanding:

Microtasks are emergency patients who need tests

The macro task is a common patient who needs to be tested

Event Loop in node

From the operating mechanism of node, we know that the ibuv library is responsible for the execution of node api

The meaning of each stage:

  • timers: This stage executes the callbacks in the timer queue such as  setTimeout() and  setInterval().
  • I/O callbacks: This phase executes almost all callbacks. But does not include close event, timer and setImmediate()callback.
  • idle, prepare: This phase is only used internally and can be ignored.
  • poll: Waiting for new I/O events, node will block here in some special cases.
  • The callback of check:  setImmediate()will be executed at this stage.
  • close callbacks: For example, socket.on('close', ...)the callback of this close event.

order of execution

When a v8 engine parses the js code and transfers it to the libuv engine, the loop first enters the poll phase. The poll phase is equivalent to the analysis of the overall synchronization code, an execution stack will be generated, and the poll queue will be cleared at the same time, and then it will go setImmediate的回调放入check队列,在setTimeout() to  setInterval()定时到期后把其回调事件放入timers队列,check stage, check and execute the check queue, check and execute the timer queue, and then enter the callbacks stage to execute the callback. The order of check and timer is not fixed and is affected by the environment in which the code runs.

After entering a new stage, the above stages will be repeated until the execution is completed and the next stage will be entered.

总结:

poll polling belongs to the io observer, process.nextTick() belongs to the idle observer, and setImmediate() belongs to the check observer.

In each round of loop checking, idle observers precede I/O observers, and I/O observers precede check observers.

When first entering the code, the idle observer does not exist.

process.nextTick() in Node

process.nextTick() is a special queue in node. These events will be executed first when each stage is completed and ready to enter the next stage. That is to say, process.nextTick() is executed when the stage is switched. And, no matter how deep the callback is, it will be executed once.

Promise

The above stage does not include Promise. In node, promise is similar to browser. It is executed after process.nextTick() and before setTimeout

Example:

const fs = require('fs')
const path = require('path')

const wait = () => new Promise((resolove, reject) => {
  setTimeout(resolove(true), 3)
})
fs.readFile(path.resolve(__dirname, './vue.config.js'), 'utf-8', async (err, data) => {
  console.log('读取的文件内容')
  await wait()
  console.log('测试测试')
  process.nextTick(() => {
    console.log('nextTick')
  })
})

setTimeout(() => {
  console.log('定时器任务0')
}, 0)


setTimeout(() => {
  console.log('定时器任务100')
}, 1000)


setImmediate(() => {
  console.log('立即执行')
})

Promise.resolve().then(() => {
  console.log('promise')
})

process.nextTick(() => {
  console.log('外层nextTick')
})

console.log('外层同步')
// 运行结果
// 外层同步
// 外层nextTick
// promise
// 定时器任务0
// 立即执行
// 读取的文件内容
// 测试测试
// nextTick
// 定时器任务100

Visual understanding

Stubborn explorer (go to the end of every room)

All codes are like mazes that have been set up, and the engine is the person who goes to explore, we call it Xiaodai.

Xiaodai arrived in the maze, and he already had a map, so he took risks according to the map.

The maze has six rooms, namely timer, i/ocallback, ide prepare (internal use, closed) , poll, check, close callback,

Among them, timer is a virtual reality room, and Xiaodai can see the scene inside at any time.

Each of the other rooms has five rooms, some are open and some are not.

Expedition rules: every time you leave a room, check for injuries ( peocess.nextTick() )

Xiaodai first enters the poll room, starts exploring (executes the poll queue), then enters the check room, timer room (random), exits after the exploration, enters the close callback, enters the io/callback room after the exploration, and finally completes the exploration and leaves .

Xiaodai said that the task was finally completed.

reference:

https://zhuanlan.zhihu.com/p/33058983

https://zhuanlan.zhihu.com/p/54882306

 

 

 

Guess you like

Origin blog.csdn.net/qdmoment/article/details/105804253