Wednesday, May 1, 2024

NodeJS Event Loop

In this tutorial we'll see what is an event loop in NodeJS and how does it help in writing asynchronous, non-blocking code.

JavaScript is single threaded

Before you go any further two important points you must know are-

  1. JavaScript is synchronous in nature.
  2. JavaScript is single threaded.

By extension NodeJS that uses V8 engine to run JavaScript can also be termed as synchronous and single threaded. For example if we have a JavaScript file as given below, code execution will happen line by line.

console.log('This is executed first');
function test(){
    console.log('In the function test')
}
//function call
test();
console.log('This is executed at the end');

On executing it output is-

This is executed first
In the function test
This is executed at the end

Asynchronous code in NodeJS

JavaScript gives you an option to write asynchronous code using callbacks where the callback function is called when the execution of the asynchronous code is finished. For example using setTimeout(), Promise or fs.readFile() in NodeJS.

With asynchronous code the execution of the code doesn't get blocked waiting for some processing to finish like I/O operation to read a file, network operation to fetch some data. Code execution can carry on with other tasks, once the asynchronous task is done, execution gets notified and then callback function can be executed with the result of the asynchronous operation.

For example in the following code fs.readFile() function is used to read a file asynchronously and setTimeout() function is used to execute code after some delay.

const fs = require('fs');
const path = require('path');

console.log('This is executed first');

setTimeout(() => {
    console.log('Displayed after delay of 10ms')
}, 10);

fs.readFile(path.join(__dirname, 'Hello.txt'), 'utf8', (err, data) => {
    if(err){
        console.error('Error while reading file', err);
        return;
    }
    console.log(data);
})

console.log('Waiting for file to be read....');

Output

This is executed first
Waiting for file to be read....
Hello from nodeJS
Displayed after delay of 10ms

Now you can see that the code lines are not executed one after the another. Even though setTimeout() function is written first but it has to be executed after 10ms so the execution doesn't block there but continue with the other function readFile(). Since fs.readFile() is also asynchronous so the execution goes to the next line and displays the message on the console. Once the file is read, then the callback function is called which displays the content of the file.

(err, data) => {
    if(err){
        console.error('Error while reading file', err);
        return;
    }
    console.log(data);
}

This is the callback function which is called with the file data and the error if there is one.

How does NodeJS manage the asynchronous operations and how are callbacks executed that's where event loop in NodeJS comes into picture.

Event Loop in NodeJS

It is the event loop in Node.js that allows Node.js to perform non-blocking I/O operations even though JavaScript is single-threaded. That is done by assigning asynchronous operations to the underlying operating system whenever possible.

Note that event loop is just a C program that is part of libuv- the C library that implements the Node.js event loop and all of the asynchronous behaviors of the platform.

Event Loop working explained

When Node.js starts, it initializes the event loop and processes the provided input script, if there are any asynchronous operations within the script those are pushed to the OS. Since most modern kernels are multi-threaded, they can handle multiple operations executing in the background.

When one of these operations (which are pushed to the OS to execute) completes, the kernel tells Node.js so that the appropriate callback may be added to the poll queue to eventually be executed.

The event loop is a loop that runs as long as your Node.js application is running. Event loop's order of operation can be divided into different phases, each phase has a FIFO queue of callbacks to execute.

When the event loop enters a given phase, it will perform any operations specific to that phase, then execute callbacks in that phase's queue until the queue has been exhausted or the maximum number of callbacks has executed. When the queue has been exhausted or the callback limit is reached, the event loop will move to the next phase, and so on.

Phases in event loop

NodeJS Event Loop
  1. timers- this phase executes callbacks scheduled by setTimeout() and setInterval().

    For example

    console.log('This is executed first');
    
    setTimeout(() => {
        console.log('Displayed after delay of 10ms')
    }, 10);
    

    In the given code callback function is this part-

    () => {
        console.log('Displayed after delay of 10ms')
    }
    
    Which has to be executed after delay of 10ms.

    This callback function is added to the queue of the timer phase and event loop will execute it after all the synchronous code is executed, delay of at least 10ms has occurred and call stack is empty.

  2. pending callbacks- This phase executes callbacks for some system operations such as types of TCP errors. Callbacks for these errors will be queued to execute in the pending callbacks phase.
  3. idle, prepare- Only used internally.
  4. poll- This phase is used to retrieve new I/O events and to execute I/O related callbacks. If the poll queue is not empty, the event loop will iterate through its queue of callbacks executing them synchronously until either the queue has been exhausted, or the system-dependent hard limit is reached.
  5. check- This phase invokes setImmediate() callbacks.
    setImmediate(() => {
        console.log('setImmediate callback');
    });
    
  6. close- Processes close callbacks. If a socket or handle is closed abruptly (e.g. socket.destroy()), the 'close' event will be emitted in this phase.

    For example-

    Server.js

    const net = require('net');
    const server = net.createServer((socket) => {
        socket.on('close', () => {
            console.log('Socket closed');
        });
    });
    
    server.on('connection', (stream) => {
        console.log('someone connected!');
    });
     
    server.listen(8000, () => {
        console.log('server bound');
    });
    

    client.js

    var net = require('net');
    var client = net.connect({port: 8000}, function() {
       console.log('connected to server!');  
       client.destroy();
    });
    

That's all for this topic NodeJS Event Loop. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Creating HTTP server in Node.js
  2. How to Setup a Node.js Project
  3. JavaScript Arrow Function With Examples

You may also like-

  1. Angular Access Control CanActivate Route Guard Example
  2. Checkbox in Angular Form Example
  3. Java Stream - boxed() With Examples
  4. Java Semaphore With Examples
  5. Reading File in Java Using Scanner
  6. Spring WebFlux Example Using Functional Programming - Spring Web Reactive
  7. Controlled and Uncontrolled Components in React
  8. Equality And Relational Operators in Java