The content of this article is about analyzing the event loop mechanism of Node.js. It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.
The event loop mechanism and some related concepts have been introduced in detail in the browser article, but it is mainly for research on the browser side. Is the same for the Node environment? Let’s take a look at a demo first:
setTimeout(()=>{ console.log('timer1') Promise.resolve().then(function() { console.log('promise1') })}, 0)setTimeout(()=>{ console.log('timer2') Promise.resolve().then(function() { console.log('promise2') })}, 0)
Compile and run it with the naked eye. The result in the browser is as follows. You already know the truth, so I won’t go into details here.
timer1 promise1 timer2 promise2
Then execute it under Node, eh. . . Strange, the running result is different from the browser~
timer1 timer2 promise1 promise2
The example shows that the event loop mechanism of the browser and Node.js is different, let’s take a look~
Event processing of Node.js
Node.js uses V8 as the parsing engine of js, and uses its own designed libuv for I/O processing. libuv is an event-driven cross-platform abstraction layer that encapsulates Some underlying features of different operating systems provide a unified API to the outside world. The event loop mechanism is also implemented in it. Core source code reference:
int uv_run(uv_loop_t* loop, uv_run_mode mode) { int timeout; int r; int ran_pending; r = uv__loop_alive(loop); if (!r) uv__update_time(loop); while (r != 0 && loop->stop_flag == 0) { uv__update_time(loop); // timers阶段 uv__run_timers(loop); // I/O callbacks阶段 ran_pending = uv__run_pending(loop); // idle阶段 uv__run_idle(loop); // prepare阶段 uv__run_prepare(loop); timeout = 0; if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT) timeout = uv_backend_timeout(loop); // poll阶段 uv__io_poll(loop, timeout); // check阶段 uv__run_check(loop); // close callbacks阶段 uv__run_closing_handles(loop); if (mode == UV_RUN_ONCE) { uv__update_time(loop); uv__run_timers(loop); } r = uv__loop_alive(loop); if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT) break; } if (loop->stop_flag != 0) loop->stop_flag = 0; return r; }
According to the official introduction of Node.js, each event loop contains 6 stages. , corresponding to the implementation in the libuv source code, as shown in the figure below
timers stage: This stage executes the callback of timer (setTimeout, setInterval)
I/ O callbacks phase: Execute some system call errors, such as network communication error callback
idle, prepare phase: Only used internally by node
poll phase: Get new I/O events, appropriate Under the conditions, the node will be blocked here
check stage: execute the callback of setImmediate()
close callbacks stage: execute the close event callback of the socket
We focus on timers and poll , Just check these three stages, because most asynchronous tasks in daily development are processed in these three stages.
timers phase
timers is the first phase of the event loop, Node Will check whether there is an expired timer, and if so, push its callback into the timer's task queue to wait for execution. In fact, Node There is no guarantee that the timer will be executed immediately when the preset time is reached, because Node's expiration check of the timer is not necessarily reliable. It will be affected by other running programs on the machine, or the main thread is not idle at that time. For example, in the following code, the execution order of setTimeout() and setImmediate() is uncertain.
setTimeout(() => { console.log('timeout') }, 0) setImmediate(() => { console.log('immediate') })
But if you put them in an I/O callback, setImmediate() must be executed first, because the poll phase is followed by the check phase.
poll phase
The poll phase mainly has two functions:
Processing events in the poll queue
When there is a timer that has timed out, execute its callback Function
even loop will synchronously execute the callbacks in the poll queue until the queue is empty or the executed callbacks reach the system upper limit (the upper limit is unknown), then the even loop will check whether there is a preset setImmediate( ), divided into two situations:
If there is a preset setImmediate(), the event loop will end the poll phase and enter the check phase, and execute the task queue of the check phase
If there is no preset setImmediate(), the event loop will block and wait at this stage
Note one detail, no setImmediate() will cause an event The loop is blocked in the poll phase, so wouldn't the previously set timer not be able to be executed? So, in the poll phase event The loop will have a checking mechanism to check whether the timer queue is empty. If the timer queue is not empty, the event loop starts the next round of event loop, that is, re-enters the timer phase.
check phase
The callback of setImmediate() will be added to the check queue. From the event loop phase diagram, we can know that the execution order of the check phase is after the poll phase.
Summary
Each stage of the event loop has a task queue
When the event loop reaches a certain stage, the task queue of that stage will be executed until the queue is cleared. Or the executed callback reaches the system upper limit before it will move to the next stage
When all stages are executed sequentially once, the event loop is said to have completed a tick
It makes sense. But without the demo, I still don’t understand it completely. I’m anxious, now!
const fs = require('fs')fs.readFile('test.txt', () => { console.log('readFile') setTimeout(() => { console.log('timeout') }, 0) setImmediate(() => { console.log('immediate') }) })
There should be no doubt about the execution results
readFile immediate timeout
Differences between Node.js and the browser’s Event Loop
Recall the previous article, microtask tasks in the browser environment The queue is executed after each macrotask is executed.
In Node.js, microtask will be executed between various stages of the event loop. That is, after a stage is executed, the tasks in the microtask queue will be executed.
demo review
Review the demo at the beginning of the article, the global script (main()) is executed, and the two timers are sequentially Put it into the timer queue, main() is executed, the call stack is idle, and the task queue starts executing;
首先进入timers阶段,执行timer1的回调函数,打印timer1,并将promise1.then回调放入microtask队列,同样的步骤执行timer2,打印timer2;
至此,timer阶段执行结束,event loop进入下一个阶段之前,执行microtask队列的所有任务,依次打印promise1、promise2。
对比浏览器端的处理过程:
process.nextTick() VS setImmediate()
In essence, the names should be swapped. process.nextTick() fires more immediately than setImmediate()
来自官方文档有意思的一句话,从语义角度看,setImmediate() 应该比 process.nextTick() 先执行才对,而事实相反,命名是历史原因也很难再变。
process.nextTick() 会在各个事件阶段之间执行,一旦执行,要直到nextTick队列被清空,才会进入到下一个事件阶段,所以如果递归调用 process.nextTick(),会导致出现I/O starving(饥饿)的问题,比如下面例子的readFile已经完成,但它的回调一直无法执行:
const fs = require('fs')const starttime = Date.now()let endtime fs.readFile('text.txt', () => { endtime = Date.now() console.log('finish reading time: ', endtime - starttime)})let index = 0function handler () { if (index++ >= 1000) return console.log(`nextTick ${index}`) process.nextTick(handler) // console.log(`setImmediate ${index}`) // setImmediate(handler)}handler()
process.nextTick()的运行结果:
nextTick 1 nextTick 2 ...... nextTick 999 nextTick 1000 finish reading time: 170
替换成setImmediate(),运行结果:
setImmediate 1 setImmediate 2 finish reading time: 80 ...... setImmediate 999 setImmediate 1000
这是因为嵌套调用的 setImmediate() 回调,被排到了下一次event loop才执行,所以不会出现阻塞。
总结
1、Node.js 的事件循环分为6个阶段
2、浏览器和Node 环境下,microtask 任务队列的执行时机不同
Node.js中,microtask 在事件循环的各个阶段之间执行
浏览器端,microtask 在事件循环的 macrotask 执行完之后执行
3、递归的调用process.nextTick()会导致I/O starving,官方推荐使用setImmediate()
The above is the detailed content of Analyzing the event loop mechanism of Node.js. For more information, please follow other related articles on the PHP Chinese website!

Python and JavaScript each have their own advantages, and the choice depends on project needs and personal preferences. 1. Python is easy to learn, with concise syntax, suitable for data science and back-end development, but has a slow execution speed. 2. JavaScript is everywhere in front-end development and has strong asynchronous programming capabilities. Node.js makes it suitable for full-stack development, but the syntax may be complex and error-prone.

JavaScriptisnotbuiltonCorC ;it'saninterpretedlanguagethatrunsonenginesoftenwritteninC .1)JavaScriptwasdesignedasalightweight,interpretedlanguageforwebbrowsers.2)EnginesevolvedfromsimpleinterpreterstoJITcompilers,typicallyinC ,improvingperformance.

JavaScript can be used for front-end and back-end development. The front-end enhances the user experience through DOM operations, and the back-end handles server tasks through Node.js. 1. Front-end example: Change the content of the web page text. 2. Backend example: Create a Node.js server.

Choosing Python or JavaScript should be based on career development, learning curve and ecosystem: 1) Career development: Python is suitable for data science and back-end development, while JavaScript is suitable for front-end and full-stack development. 2) Learning curve: Python syntax is concise and suitable for beginners; JavaScript syntax is flexible. 3) Ecosystem: Python has rich scientific computing libraries, and JavaScript has a powerful front-end framework.

The power of the JavaScript framework lies in simplifying development, improving user experience and application performance. When choosing a framework, consider: 1. Project size and complexity, 2. Team experience, 3. Ecosystem and community support.

Introduction I know you may find it strange, what exactly does JavaScript, C and browser have to do? They seem to be unrelated, but in fact, they play a very important role in modern web development. Today we will discuss the close connection between these three. Through this article, you will learn how JavaScript runs in the browser, the role of C in the browser engine, and how they work together to drive rendering and interaction of web pages. We all know the relationship between JavaScript and browser. JavaScript is the core language of front-end development. It runs directly in the browser, making web pages vivid and interesting. Have you ever wondered why JavaScr

Node.js excels at efficient I/O, largely thanks to streams. Streams process data incrementally, avoiding memory overload—ideal for large files, network tasks, and real-time applications. Combining streams with TypeScript's type safety creates a powe

The differences in performance and efficiency between Python and JavaScript are mainly reflected in: 1) As an interpreted language, Python runs slowly but has high development efficiency and is suitable for rapid prototype development; 2) JavaScript is limited to single thread in the browser, but multi-threading and asynchronous I/O can be used to improve performance in Node.js, and both have advantages in actual projects.


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

mPDF
mPDF is a PHP library that can generate PDF files from UTF-8 encoded HTML. The original author, Ian Back, wrote mPDF to output PDF files "on the fly" from his website and handle different languages. It is slower than original scripts like HTML2FPDF and produces larger files when using Unicode fonts, but supports CSS styles etc. and has a lot of enhancements. Supports almost all languages, including RTL (Arabic and Hebrew) and CJK (Chinese, Japanese and Korean). Supports nested block-level elements (such as P, DIV),

SublimeText3 Linux new version
SublimeText3 Linux latest version

Dreamweaver Mac version
Visual web development tools

MantisBT
Mantis is an easy-to-deploy web-based defect tracking tool designed to aid in product defect tracking. It requires PHP, MySQL and a web server. Check out our demo and hosting services.

Notepad++7.3.1
Easy-to-use and free code editor
