Node Js — Event Loop Explained

Deepakpaliwal
4 min readApr 10, 2021

--

Node js is a single-threaded language. Despite being single-threaded it is asynchronous and has non-blocking I/O operations. These non-blocking I/O operations are managed using an event loop and are sent to the system kernel. As soon as the system kernel completes the process it tells node js that the process is complete and a callback function of that operation is executed.

Note : System kernel are multi-threaded so they can handle multiple operations and this helps node js do parallel executions.

The event loop checks if the call stack is empty or if there is some function in it. Every function executes only when it comes to the call stack. Let’s look at the example of how it actually works.

function a(){  console.log("1")}console.log("2")a()//output will be 
//1
//2

As soon as the code starts after the function gets defined and after that, the first console statement is executed it prints “1” to the console. As soon as the code gets executed the function is pushed to the call stack and the function starts executing. It executes the only line in the function and prints the “2” on the console.

This is simple as there is no async function in this code. Let’s have a look at the async example.

console.log("a")setTimeout(()=>{    console.log("c")},0)console.log("b")//Output
//a
//b
//c
  • The first console executes normally and prints “a” on the console.
  • As soon as it encounters the setTimeout it is pushed to call stack and is not executed normally like other functions. It is sent to the system to execute. Here we haven’t waited for the setTimeout to complete its execution.
  • After setTimeout is sent to the system the call stack is empty and so the last console is executed and it prints “b” on the console
  • As the timer of setTimeout is finished the system tells the node js that it is completed and its callback is sent to the event queue. Here event loop checks whether the call stack is empty or not. As soon as the call stack is empty the callback is pushed to the call stack and the callback function is executed normally like other functions until we get another async function.

Note : Even when we assign the setTimeout timer for 0 ms it still didn’t run immediately. This is because the setTimeout takes some time to assign timer by the system and as we have provided the timer to be 0 then it is immediately send to the event queue. Then event loop checks if the call stack is empty. At this time the other codes are already pushed to the call stack and are executed line by line. Basically the async code always starts running after all the sync code gets executed first.

Event Loop Phases

When we have a lot of callbacks and async functions then there may be a chance that two or more callbacks of async functions have to execute at the same time but it is not possible to execute both at the same time. So in this case the event loop phases come in hand.

event-loop phases

Every phase of the event loop has a queue/heap which is used to push the callbacks related to that phase. For example, timer callbacks are stored in the timer phase queue. The event loop iterates again and again through all these phases and checks if there is any function to execute. One iteration of these phases is called tick. So every tick starts with timers and ends at the close callback phase.

Timer Phase

This phase is the first phase of the event loop. It stores all the callbacks of the timer functions which are expired and starts executing them according to the delay of their timers.

Pending callbacks

In this phase, the callbacks which are related to I/O operations are executed. For example, if you have a callback that has to run after some read or write operation the callback and the error callback is sent here.

Idle/Prepare: In this phase, the event loop does nothing but sit idle and prepares to go to the next phase which is the Poll phase. It doesn't have any queue for this phase.

Poll Phase

This is the most important phase of all the phases. Almost all callbacks here except for timers are executed here. In this phase, if there are callbacks in its queue then it executes them one by one until it becomes empty. If the queue of this phase is empty then the event loop waits here for some time. During this time the event loop checks whether there is any callback on the Check/setImmediate(Its next phase). If it finds any callback on that phase it immediately goes to that phase and starts executing all the callbacks until no callback is left. And if it won’t find any callback in that phase it checks whether if there are expired timers and are waiting for their callback to execute and if it finds any it moves to the next phase and does the same thing as before.

Check/setImmediate

In this phase, the setImmediate callbacks are executed and run until the queue is empty.

Close Callbacks

This phase is generally for the closing events in node js, like socket.on('close', fn) or process.exit() .

--

--