When we start a node.js application, it loads the event loop and adds the necessary callbacks to the call stack. In this article, you will learn in detail how the event loop in node.js works.
Here are just a few reasons why learning the event loop is important:
According to __node.js's official __website, the event loop allows node.js to perform non-blocking I/O operations—despite the fact that JavaScript is single-threaded—by offloading operations to the system kernel whenever possible.
Let's break down this definition into three keywords:
Non-blocking I/O operations
A program is said to be non-blocking if the execution of an operation is not obstructed. Since we have mentioned non-blocking here, it’s important to also mention what blocking is. It simply means you have to complete an operation after another has been completed. Refer to the __official __site for more detail on this.
Single-threaded
A program is said to be single-threaded if it has only one call stack and is able to perform one task at a time if it uses the FIFO concept. This means the first program will always be run before the next one on the stack. Though javascript might appear to be a single-threaded language, it solely depends on the environment in which it’s been run.
Kernel
The kernel in this context simply means the operating system the program is running on. Javascript is single-threaded, but node.js has the ability to not block the thread when performing multiple input and output (I/O) operations. It does that by offloading this operation to another operating system (e.g., Linux, Windows, Mac OS X, etc.) whenever possible. Operations are mostly offloaded into the OS; this is what differentiates Javascript from node.js.
When we start up the node application, the event loop starts to run right away. The event loop has multiple phases, and each phase has callback queues to execute. When the event loop is added to a particular phase, it will carry out some operations on that particular phase, then execute some callbacks in that phase queue.
This will continue until the queue is empty or the maximum number of callbacks have been executed. When the limit is reached, the event loop moves to the next phase to carry out this same operation.
The four most important phases:
Expiring time callback
This phase takes care of callbacks from expired timers;
For example,
setTimeout (()=> console.log ('expired timers'), 1000)
is a function that sets the timer to expire after a certain number of seconds.
So if there are callback functions from the timers that just expired, those are the first ones to be processed by the event loop.
If the timer expires later, during the time when one of the other phases has been processed, then the callback of that timer will only be called as soon as the event loop comes back to the first phase. It works like this in all four phases.
I/O polling and callbacks
Polling basically means searching for new I/O events that are ready to be processed and putting them on the callback queues. It is critical to understand that in the context of the node app, I/O simply refers to things like networking and file access.
For example,
fs.readFile('file.txt', (e, d)) =>
console.log(‘File read’)
})
It’s in this phase where 99% of our code gets executed because, in a typical node app, the bulk of what we need to do is related to networking and reading files.
setImmediate Callbacks
We use this special timer if we want to process callbacks immediately after the I/O of the polling and execution phases. which can be important in some more advanced cases.
setImmediate(() => setImmediate(() =>
console.log('immediate');
});
close callbacks
In this phase, all close events are processed, for example, when a web server shuts down. This completes the fourth phase of the event loop.
Note: There are other events that the node.js event loop uses internally, but the four above are what matters to us as far as the article is concerned.
With the above-mentioned, we finished the tick, which is just one cycle in the event loop. After this cycle, node.js decides whether to continue with the loop or exit the loop.
Node does that simply by checking if there are any tasks (e.g., timers or I/0 tasks) that are still running in the background. And if there aren’t any, it will just exit the application. If there are pending tasks, they will continue on to the next one, e.g., when working with HTTP requests or reading files.
This is basically what the node event loop is all about.
Because everything in node.js eventually runs in a single thread, you can have millions of users accessing the same pool at the same time, making node.js very lightweight and scalable. But at the same time, it comes with the danger of blocking a single thread, which would make the entire app slow down and even stop the app.
It is your responsibility as a developer not to block the event loop; the following guidelines will help you avoid blocking the event loop.
The event loop is what makes asynchronous programming possible in node.js. making it the most important feature in Node's design. This makes node.js completely different from other platforms.
It takes care of all incoming events and performs orchestration by offloading heavier tasks into the thread pool and doing the most simple work itself.