Home >Web Front-end >JS Tutorial >Detailed explanation of event loop in JS and Node.js
The
event loop in
js leads to the problem that the execution results are different when running programs with setTimeout
and Promise
in chrome and node, which leads to the problem Based on the event loop
mechanism of Nodejs, this article gives you a detailed analysis of the principles and usage of events in JS and Node.js through examples. I hope it can help you.
console.log(1) setTimeout(function() { new Promise(function(resolve, reject) { console.log(2) resolve() }) .then(() => { console.log(3) }) }, 0) setTimeout(function() { console.log(4) }, 0) // chrome中运行:1 2 3 4 // Node中运行: 1 2 4 3
The execution results of chrome and Node are different, which is very interesting.
1. Task queue in JS
One of the major features of JavaScript language is single thread, that is to say, only one thread can be executed at the same time. Do one thing. So why can't JavaScript have multiple threads? This can improve efficiency.
The single thread of JavaScript is related to its purpose. As a browser scripting language, JavaScript's main purpose is to interact with users and manipulate the DOM. This determines that it can only be single-threaded, otherwise it will cause very complex synchronization problems. For example, suppose JavaScript has two threads at the same time. One thread adds content to a certain DOM node, and the other thread deletes the node. In this case, which thread should the browser use?
Therefore, in order to avoid complexity, JavaScript has been single-threaded since its birth. This has become the core feature of this language and will not change in the future.
In order to take advantage of the computing power of multi-core CPUs, HTML5 proposes the Web Worker standard, which allows JavaScript scripts to create multiple threads, but the child threads are completely controlled by the main thread and must not operate the DOM. Therefore, this new standard does not change the single-threaded nature of JavaScript.
2. Task queue event loop
Single thread means that all tasks need to be queued, and the previous task will not be executed until the end of the previous task A mission. If the previous task takes a long time, the next task will have to wait.
So, all tasks can be divided into two types, one is synchronous task (synchronous) and the other is asynchronous task (asynchronous). Synchronous tasks refer to tasks queued for execution on the main thread. The next 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, only when the "task queue" notifies the main thread that an asynchronous task can be executed, will the task enter the main thread for execution.
Specifically, the operating mechanism of asynchronous execution is as follows. (The same goes for synchronous execution, since it can be viewed as asynchronous execution without asynchronous tasks.)
All synchronous tasks are executed on the main thread, forming an execution context stack. In addition to the main thread, there is also a "task queue". As long as the asynchronous task has running results, an event is placed in the "task queue". Once all synchronization tasks in the "execution stack" have been executed, the system reads the "task queue" to see what events are in it. Those corresponding asynchronous tasks end the waiting state, enter the execution stack, and start execution. The main thread keeps repeating the third step above.
#As long as the main thread is empty, it will read the "task queue". This is the running mechanism of JavaScript. This process keeps repeating.
3. Timer setTimeout
and setInterval
The timer function is mainly composed of setTimeout()
and setInterval()
are two functions. Their internal operating mechanisms are exactly the same. The difference is that the code specified by the former is executed once, while the latter is executed repeatedly.
setTimeout(fn,0)
means to specify a task to be executed in the earliest available idle time of the main thread, that is, to be executed as early as possible. It adds an event at the end of the "task queue", so it will not be executed until the synchronization task and the existing events in the "task queue" have been processed.
HTML5
The standard stipulates that the minimum value (shortest interval) of the second parameter of setTimeout()
shall not be less than 4
milliseconds, If it is lower than this value, it will automatically increase. Prior to this, older versions of browsers set the minimum interval to 10
milliseconds. In addition, those DOM changes (especially those involving page re-rendering) are usually not executed immediately, but every 16 milliseconds. At this time, the effect of using requestAnimationFrame()
is better than setTimeout()
.
It should be noted that setTimeout() only inserts the event into the "task queue". The main thread must wait until the current code (execution stack) is completed before the main thread executes the callback function it specifies. If the current code takes a long time, it may take a long time, so there is no way to guarantee that the callback function will be executed at the time specified by setTimeout().
4. Event Loop of Node.js
Event polling is mainly for polling the event queue. The event producer queues the events into the queue. There is a thread at the other end of the queue called the event consumer, which will continuously query whether there are events in the queue. If there are events, It will be executed immediately. In order to prevent blocking operations from affecting the current thread's reading queue during execution, the event consumer thread will entrust a thread pool to specifically perform these blocking operations.
The mechanism of Javascript front-end and Node.js is similar to this event polling model. Some people think that Node.js is single-threaded, that is, the event consumer is single-threaded. Polling, what should I do if there is a blocking operation? Doesn't it block the current single-threaded execution?
In fact, there is a thread pool at the bottom of Node.js. The thread pool is specially used to perform various blocking operations. This will not affect the single-threaded main thread to poll the events in the queue and execute some tasks. After the thread pool completes the operation, it will serve as the event producer and put the operation results into the same queue.
In short, an event polling Event Loop requires three components:
Event queue Event Queue, which belongs to the FIFO model. One end pushes event data and the other end pulls out event data. The two ends only communicate through this queue, which is an asynchronous loose coupling. The queue's reading polling thread, the event consumer, and the protagonist of the Event Loop. A separate thread pool, Thread Pool, is specially used to perform long tasks, heavy tasks, and heavy physical work.
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.
V8 engine parses JavaScript scripts. The parsed code calls the Node API. The libuv library is responsible for the execution of the Node API. It allocates different tasks to different threads to form an Event Loop (event loop), and returns the execution results of the tasks to the V8 engine in an asynchronous manner. The V8 engine then returns the results to the user.
We can see that the core of node.js is actually the libuv
library. This library is written in C and can use multi-threading technology, while our Javascript application is single-threaded.
Nodejs’s asynchronous task execution process:
The code written by the user is single-threaded, but nodejs is not single-threaded internally!
Event mechanism:
Node.js does not use multiple threads to perform work for each request. Instead, it adds all work to an event queue, and then has A separate thread to loop through the events in the queue. The event loop thread grabs the top entry in the event queue, executes it, and then grabs the next entry. When executing long-running or blocking I/O code
In Node.js, because there is only a single thread constantly polling the queue for events, for I/O operations such as database file systems, Including HTTP requests and other operations that are easy to block and wait for, if they are also implemented in this single thread, they will definitely block the execution of other tasks. Javascript/Node.js will delegate it to the underlying thread pool for execution, and will tell the thread pool A callback function, so that the single thread continues to perform other things. When these blocking operations are completed, the results are put into the queue together with the provided callback function. When the single thread continues to read events from the queue, these blocked operations are read After operating the results, these operation results will be used as input parameters of the callback function, and then the callback function will be activated to run.
Please note that the single thread of Node.js is not only responsible for reading queue events, but also executing the callback function. This is a major feature that distinguishes it from multi-threaded mode. In multi-threaded mode, single thread The thread is only responsible for reading queue events, and no longer does other things. It will entrust other threads to do other things, especially in the case of multi-core, one CPU core is responsible for reading queue events, and one CPU core is responsible for executing activated tasks. In this way Best for CPU-intensive tasks. In turn, the execution activation task of Node..js, that is, the task in the callback function, is still executed in the single thread responsible for polling, which is destined to prevent it from performing CPU-heavy tasks, such as converting JSON to other data formats, etc. , these tasks will affect the efficiency of event polling.
5. Nodejs features
RESTful API是NodeJS最理想的应用场景,可以处理数万条连接,本身没有太多的逻辑,只需要请求API,组织数据进行返回即可。
6. 实例
看一个具体实例:
console.log('1') setTimeout(function() { console.log('2') new Promise(function(resolve) { console.log('4') resolve() }).then(function() { console.log('5') }) setTimeout(() => { console.log('haha') }) new Promise(function(resolve) { console.log('6') resolve() }).then(function() { console.log('66') }) }) setTimeout(function() { console.log('hehe') }, 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') }) }) new Promise(function(resolve) { console.log('13') resolve() }).then(function() { console.log('14') }) // node1 : 1,7,13,8,14,2,4,6,hehe,9,11,5,66,12,haha // 结果不稳定 // node2 : 1,7,13,8,14,2,4,6,hehe,5,66,9,11,12,haha // 结果不稳定 // node3 : 1,7,13,8,14,2,4,6,5,66,hehe,9,11,12,haha // 结果不稳定 // chrome : 1,7,13,8,14,2,4,6,5,66,hehe,9,11,12,haha
chrome的运行比较稳定,而node环境下运行不稳定,可能会出现两种情况。
chrome运行的结果的原因是Promise
、process.nextTick()
的微任务Event Queue运行的权限比普通宏任务Event Queue权限高,如果取事件队列中的事件的时候有微任务,就先执行微任务队列里的任务,除非该任务在下一轮的Event Loop中,微任务队列清空了之后再执行宏任务队列里的任务。
相关推荐:
The above is the detailed content of Detailed explanation of event loop in JS and Node.js. For more information, please follow other related articles on the PHP Chinese website!