Node.js Event Loop

An Introduction to Event Loop

As we have already discussed, Node.js is an Asynchronous platform with a non-blocking I/O. Node.js has awesome features that made it this thriving, and the Event Loop is one of the most significant features. The JavaScript code of Node.js is single-threaded, which indicates that only one thing can process at a time. This drawback is very helpful because it not only simplifies the program but also reduces the concurrency issues.

As most modern kernels are multi-threaded, they can manage numerous processes running in the background. When one of these processes ends, the kernel sends the message to Node.js so that the suitable callback may be included in the poll queue to be processed.

Normally, there is an event loop present in most browsers for every browser tab, making every process isolated and avoiding a web page with heavy execution or infinite loops to block the whole browser. The environment handles numerous concurrent event loops for handling Application Programming Interface (API) calls just in case. Web Workers run in their event loop as well.

The Concept of blocking the event loop

Any script file of JavaScript that takes a very long time to set back control to the event loop would block the processing of any script on the page, even it can block the UI thread, and the user cannot scroll the page, click around and many more.

In JavaScript, most of the I/O primitives are non-blocking, including filesystem processes, network requests, and many more.  JavaScript is centered on callbacks, and more recently, on async/await, and promises as being blocking are the exception.

Understanding the call stack

The call stack is a ‘Last In, First Out’ (LIFO) data structure comprises the address at which the process will continue and often occur local parameters and variables from each call.

The event loop regularly inspects the call stack to check whether there is any function that needs to be executed or not. This process includes various functions that it discovers to the call stack and processes them in order.

Understanding the Node.js Event-Driven programming

Before we jump into the Node.js event loop concept and its working, let's discuss Node.js' various parts as a whole.

  • Chrome V8 Engine: Chrome V8 Engine is a JavaScript open-source, high-performance engine designed for Google Chrome and is written in C++.
  • The C libuv library: libuv is a library primarily developed for Node.js that supports multiple platforms and focuses on asynchronous I/O.
  • The Operating System: Operating System is system software used to manage computer software and hardware resources and provide common computer programs. The Operating System is a requirement for all the computer programs to function, except the firmware.

The execution of JavaScript code depends upon the Chrome V8 engine. It works as a core for Node.js, and it’s functioning as it can be embedded into a C++ application. The V8 Engine takes input as a string in JavaScript, processes it and then prints the output to stdout. Though, there is a problem with this as of the Synchronous working of JavaScript.

However, one can ask a question – While performing asynchronous operations,

How does Node.js allow V8 Engine to execute synchronous JavaScript code?

To answer the above question, let’s see a diagrammatic illustration of the communication channel between the several components of Event-Driven I/O of Node.js, given below.

Node.js Event Loop

The whole process of diagram can be broken into steps, which are as follows:

  1. A JavaScript code runs on a V8 engine, which needs an asynchronous task to be executed.
  2. A request is submitted by the Node.js libuv library to the OS for performing the task.
  3. This task is then placed onto a queue of events that will finish the task later in the future.
  4. The event loop consistently inspects the queue of events to see whether any task in the queue is completed or not.
  5. If any of the tasks is found completed by the event loop, it is returned for continuing the execution of the resultant JavaScript callback.

Hence, from observing the above diagram and understanding the points, we can conclude that the Node.js event loop is nothing but an endless looping process that checks the event queue to see if any processes has been completed or not. Let’s see an example given below:

File: eventloop.js

// Importing the 'fs' module
const fs = require('fs');
// Assigning the path of the file to be read
const mypath = './myWorld.txt';
console.log('Reading a file');
// Asynchronous version to read a file
fs.readFile(mypath, 'utf8', function( err, data) {
  if (err) {
    return console.error(err);
  };
  console.log('Asynchronous Read: ' + data);
});
console.log('The File has been read successfully!!');

In the above example, we have created a simple program to read a file. The point is to be noted that the V8 engine continues executing the JavaScript operations outside the callback function. When the asynchronous task is to finish, the Node.js event loop informs the V8 engine and executes the callback.

To see the output of the above program, type the following command on Node.js Command-line:

$ node eventloop.js

The Output of same should look as follows:

Reading a file
The File has been read successfully!!
Asynchronous Read: Hello! Welcome to my World.

The Phases of Event Loop

  • Timers: The timers phase processes callbacks scheduled by setInterval() and setTimeout() respectively.
  • Pending callbacks: This phase processes I/O callbacks referred to the following loop iteration.
  • Idle, prepare: This phase is used internally only.
  • Poll: The poll phase calculates the time for blocking and polling for I/O and then processes events present in the poll queue.
  • Check: The check phase allows the user to process callbacks instantly after the poll phase is completed, using setIntermediate() callbacks.
  • Close callbacks: The close callbacks phase emits the ‘close’ event if the socket or handles are closed unexpectedly. E.g., socket.on(‘close’,...).