Node.js TypeScript #9. The Event Loop in Node.js

JavaScript Node.js

This entry is part 9 of 10 in the Node.js TypeScript

JavaScript and the Node.js environment uses the Event Loop. In this article, we explore the idea of it and go through different phases that the event loop has. Although this series aims to present the usage of Node.js with TypeScript, this time we don’t get much of a chance to do it. In this part of the course, we cover  , and   functions. To do it, we briefly go through different phases of the event loop to illustrate the differences between the above functions.

The event loop in Node.js

The event loop is a mechanism that browsers also implement, but in this article, we focus on the implementation that Node.js environment uses, done by the libuv library. It was designed for use in Node.js, but now it is a separate project.

Node.js constantly executes the event loop, and every iteration of it we call a tick. If during an iteration events wait in the queue, they are invoked one by one. Node.js initializes the event loop on start, and each iteration has a set of phases defining an order of operations. Every phase has its queue of functions to execute. There are many opportunities for an event to be put into the event loop: some of the best examples are the   function and callbacks for the Ajax requests. Turns out, they have their separate place in the Event Loop. Let’s dig into it!

node js event loop diagram


The first phase is the timers. During that phase, the event loop executes callbacks scheduled by the  and . Since we often use the above functions, let’s look into them more.


The simple way of looking into the   would be to state that it sets a timer that executes a function once the timer expires. The above is too much of an oversimplification. The   function specifies a timer with a threshold after which the provided callback should be executed rather than the exact time. To understand it, we need to take into consideration how the event loop works.

In the example above we call  twice. The first time, we measure the time between calling the   function and the callback. It turns out that it is over 100ms. This because when we call the   function, it sets up a timer after which it pushes the callback to the timers phase queue. The issue here is that when that happens, there is another function running, and therefore the next function in the queue needs to wait.


The   works similarly, but it causes the function to execute repeatedly with a certain time delay between each call. It returns an id that we can use to stop the execution.

The intriguing part is that we can do a similar thing using the   function:

The   delays the function regularly regardless of the state of the previous function call. If   is timed to deliver every 1000ms and the execution takes 500ms, the actual interval between the end of the call and the next invocation is 500ms.

The recursive  , on the other hand, schedules a new function call when the previous one ends. The longer the previous function took to complete, the bigger the interval between those two functions starting.

Pending callbacks, idle, and prepare

After the timers phase, there go a few of less crucial phases for us, beginning with the pending callbacks phase. It executes callbacks for some systems operations, such as TCP errors. The next phases we call idle and prepare, but the Node.js only uses them internally. If you want to dig deeper, you can read through the introduction to libuv.


Input/Output related callbacks execute during the poll phase – for example, the ones connected to the File System module. If there are any events, they are handled one by one. When that’s not the case, Node.js waits a bit for new events to come up here and execute them immediately. If any scripts have been scheduled using the  function, the Node.js does not wait and goes straight to the check phase.


The check phase runs immediately after the poll phase and invokes callbacks set up using the   function. It is designed to execute a script once the current poll phase completes. It is a bit more performant than using   , because it does not have to start a timer of any sort.

Let’s try to run both of them and compare the results:

If we run the above code multiple times, we can observe that it is not predictable which one of the callbacks runs first. There is a situation in which the  function might be considered to have an advantage. If we run both   and   within the I/O cycle (for example in a callback after reading a file), the latter is always called first:

set immediate

set timeout

The above behavior is due to the fact, that the   callback runs in the poll phase. The next phase after that is check, so if we attach some callbacks to it using the  function, it is guaranteed to be first.

Close callbacks

In this phase, the close callbacks run. An example of that is the ‘close‘ event emitted when a socket is closed using the   function.

If you would like to know more about websockets, check out Introduction to WebSockets. Creating a Node.js server and using WebSocket API in the browser


Another similar function is the  . It is not mentioned in the diagram at the top of the article, because it is not technically part of the event loop. When we look into the documentation, we can see that the   fires immediately on the same phase that the event loop currently is. It means that it invokes more immediately than   and even the official documentation acknowledges that those functions should be swapped. This is not going to happen though because it would break a lot of existing code.


In this article, we went through a lot of different ways to interact with the event loop. We used the   and  functions that make use of timers and described differences between them. We also covered the  function that sets up callbacks for the Check phase, and how it interferes with the Poll phase. This, along with the knowledge of how the  works can give you an idea of differences between those functions and how does the event loop works. Although when discussing the above functions, we briefly went through different phases in the event loop, I encourage you to check out the official documentation for details.

Series Navigation<< Node.js TypeScript #8. Implementing HTTPS with our own OpenSSL certificateNode.js TypeScript #10. Is Node.js single-threaded? Creating child processes >>
Notify of