Event Loop
Purpose of the Event Loop
JavaScript is single-threaded, and a task waits for the previous task to finish before executing, which can be called synchronous I/O or blocking I/O.
The Event Loop provides a non-blocking I/O running mode.
Unlike multithreading, where you can imagine an Event Loop is a server serving several tables, multithreading is a server serving several tables.
The Event Loop is introduced
Each of the following calls triggers an Event loop:
- Asynchronous API call
- Timer arrangement
process.nextTick()
Here is a diagram of the event Loop’s internal execution:
┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ ┌ ─ > │ timers │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ │ pending Callbacks │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ │ idle, Prepare │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ incoming: │ │ │ poll │ < ─ ─ ─ ─ ─ ┤ connections, │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ data, Etc. │ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ │ check │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ └ ─ ─ ┤ close callbacks │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘Copy the code
Each event loop is called a tick.
Each stage has a callback queue, and when the Event loop reaches a certain stage, it will execute the callback in the queue on a first-in, first-out basis, and there is a mechanism limit on the number of executions.
Overview of each stage
- timers: perform
setTimeout()
和setInterval()
Planned callbacks - Pending: Perform some callback about system operations (typically I/O requests)
- Idle, prepare: used only by the Node
- Poll: Searches for new I/O events
- check: perform
setImmediate()
The callback - close: Execute all
.on('close')
Event callback
Node checks between each event loop to see if there is an asynchronous operation or timer, and shuts down if there isn’t.
The timer timers
A timer specifies the starting point for the callback to execute, and the callback will run as scheduled as possible, but mostly a little late. Callbacks are not called exactly at a given time.
The poll phase actually controls when the timer executes.
Here’s an example:
const fs = require('fs');
setTimeout(() = > {
console.log('finish timeout')},100)
// Suppose 'fs.readfile ()' uses 95 ms to read the file
fs.readFile('./text'.() = > {
// Assume that the callback takes 10 ms to execute
console.log('finish read file')})Copy the code
In this example, a timeout timer starts at 100 ms, and then asynchronous file reads begin. When the Event loop enters the poll phase, the phase queue is actually empty (fs.readfile () has not yet finished executing), so it waits until the start point of the timer is reached. 95 ms past, fs.readfile () finishes reading the file and adds its callback to the poll queue and executes. When the callback is completed in 10 ms and the queue is empty again, the Event loop returns to the Timers stage to execute the timer callback. The callback to the timer will then start executing at 105 MS.
To prevent event loop “starvation” caused by the continuous running of the poll phase, libuv (the implementation of Node.js Event Loop) sets a maximum value (depending on the system) to stop polling for more events.
Pending Callbacks to be executed
This phase performs some callbacks about system operations (typically I/O requests).
When an asynchronous operation (such as fs.readfile) is performed, Node sends an I/O request to the system. When the I/O operation ends or an error is encountered, the callback for the asynchronous operation is placed in the pending queue and executed in the next pending Callbacks.
Polling poll
The poll phase has two main functions:
- Calculate how long it should block and poll I/O, and then
- Run the event in the poll queue
When the Event loop enters the poll phase and there is no scheduled timer, one of the following two events will occur:
- If the poll queue is not empty, the Event Loop synchronously iterates through the callbacks in the queue, within the maximum number limit (system dependent)
- ifpollIf the queue is empty, one of two things will happen:
- If the script is
setImmediate()
If it is planned, it will end immediatelypollPhase, and then entercheckStage to executesetImmediate()
The callback - If the script is not
setImmediate()
The event loop will wait for the callback to be queued and then execute it immediately
- If the script is
Check the check
This phase allows the developer to perform some callbacks immediately (after the poll phase), and if the poll phase is idle or setImmediate() piles up, the Check phase will continue instead of waiting (as described above).
SetImmediate () is actually a special timer that runs in a separate phase.
In summary (highlighted in the Node documentation), when the code executes, the Event loop ends up in the poll phase waiting for incoming connections, requests, and so on. However, if a callback is scheduled by the setImmediate() scheme, the poll phase immediately terminates and then the Check phase proceeds.
Close Callbacks
The close event is raised during this phase if the socket or handle is suddenly closed. This can also be triggered by process.nexttick (). `
setImmediate()
vs setTimeout()
The two functions are similar, but behave differently in different scenarios:
setImmediate()
Is designed to be used inpollPost-phase executionsetTimeout()
Used to execute scripts after a period of time
In general, the timing capability is limited by system performance, and in non-I /O cycles (such as the main module), the order in which the two timers are executed is not determined:
setTimeout(() = > {
console.log('timeout');
}, 0)
setImmediate(() = > {
console.log('immediate');
})
Copy the code
But in an I/O cycle, setImmediate() always executes first:
const fs = require('fs')
fs.readFile(__filename, () = > {
setTimeout(() = > {
console.log('timeout')},0);
setImmediate(() = > {
console.log('immediate') // output first
});
});
Copy the code
process.nextTick()
Process.nexttick () is not technically part of the Event loop. Instead, nextTickQueue will run after the current operation completes, regardless of the current event loop phase. The operation here is defined as a transition to a C/C++ handle that then handles what JavaScript is supposed to do.
When process.nexttick () is called at any stage, its callback is executed before the Event loop continues. However, misuse of this mechanism can starve I/O operations and prevent event loops from entering the poll phase.
process.nextTick()
vs setImmediate()
Remember that process.nexttick () executes immediately in the current phase and is a little more “immediate” than setImmediate().
Node recommends using setImmediate() in all situations because it’s easier to reason.
Why useprocess.nextTick()
- Allows the developer to handle errors, clean up unwanted resources, or try to initiate the request again before the Event loop continues
- It may be necessary to execute callbacks after the Call stack has expanded and before the Event loop continues
reference
The Node.js Event Loop, Timers, and process.nextTick() – Node.js
What is an Event Loop? – nguyen half-stretching
A complete guide to the Node.js event loop – Piero Borrelli
What you should know to really understand the Node.js Event Loop – Daniel Khan
How the Event Loop Works in Node.js – heynode