Profile Photo

How Nodejs Works internally

Created on: Jul 30, 2024

Nodejs Event loop

To understand how Node.js functions, it's essential to grasp its key components:

  • V8 Engine: This is the JavaScript engine developed by Google, responsible for executing JavaScript code efficiently.
  • Libuv: A cross-platform C library that handles asynchronous I/O operations, networking, and file systems.
  • Event Loop: The heart of Node.js, it continuously monitors the call stack and the event queue, managing asynchronous operations.
  • Callback Queue: Stores callbacks for asynchronous operations that have completed.
  • Thread Pool: A limited number of threads for handling blocking operations.

The Event Loop Mechanism

The event loop is the cornerstone of Node.js's performance. Here's a simplified breakdown of how it works:

  1. Code Execution: When a Node.js application starts, the code is executed synchronously.
  2. Asynchronous Operations: When an asynchronous operation (like file reading, network request) is encountered, Node.js offloads it to Libuv, which handles the operation in the background.
  3. Callback Registration: The callback function for the asynchronous operation is registered in the event queue.
  4. Event Loop Iteration: The event loop continuously checks the call stack. If it's empty, it picks the first callback from the event queue and pushes it to the call stack for execution.
  5. Completion: Once the callback finishes, it's removed from the call stack, and the event loop continues checking for new callbacks.

Let's understand looping machanism using a example.

const fs = require("fs"); console.log("Start of the script"); fs.readFile("example.txt", "utf8", (err, data) => { if (err) { console.error("Error reading file:", err); return; } console.log("File content:", data); }); console.log("End of the script");
  1. When the Node.js application starts, it executes the code synchronously, line by line. console.log('Start of the script'); is executed immediately, and "Start of the script" is logged to the console.

  2. When Node.js encounters fs.readFile, it offloads this I/O operation to Libuv, which handles file reading in the background. The callback function provided to fs.readFile is registered to be executed once the file reading operation completes.

  3. The callback function for the file reading operation is registered in the event queue. The main thread continues to execute the remaining code synchronously.

  4. After logging "End of the script" to the console, the synchronous part of the code is done, and the call stack is empty. The event loop then checks the event queue for any callbacks that need to be executed.

  5. When the file reading operation completes, the event loop picks up the callback function from the event queue and pushes it to the call stack. The callback function is executed, logging the file content to the console. Once the callback finishes execution, it is removed from the call stack, and the event loop continues to check for other pending callbacks.

Non-Blocking I/O

Node.js excels at handling I/O-bound operations efficiently due to its non-blocking nature. Instead of waiting for an operation to complete, Node.js registers a callback and moves on to the next task. When the operation finishes, the callback is added to the event queue.

Thread Pool

While Node.js is primarily single-threaded, it maintains a thread pool for computationally intensive tasks. These tasks are offloaded to the thread pool to prevent blocking the event loop. We can use Worker Threads and child process to handle cpu intensive task.

Nodejs is platform independent

Node.js is built on the V8 JavaScript engine, which is designed to be platform-independent. The V8 engine compiles JavaScript code to native machine code, which allows it to run on different operating systems.

Node.js relies on the libuv library for its asynchronous I/O operations. Libuv abstracts the differences between various operating systems, providing a uniform API for file system operations, networking, and other system tasks. This abstraction layer ensures that Node.js applications can run on multiple platforms without modification.

While Node.js itself is platform-independent, some native modules or third-party packages might be platform-specific. For instance, modules that interact with system-level APIs or use platform-specific features may have compatibility issues across different operating systems. some of those modle are node-gyp, serialport, ffi-napi.