How Nodejs Work Internally ⭐

How Nodejs Work Internally ⭐

Hey Everyone In this Article You will understand in Depth How Nodejs Work Internally Before starting to understand Node.js, let me explain it briefly.

What is Nodejs

Node.js is a powerful and popular environment for building fast, scalable network applications. It is built on Chrome's V8 JavaScript engine, and it uses an event-driven, non-blocking I/O model, making it efficient and suitable for real-time applications. In this article, we will explore the internal workings of Node.js, providing insights for both beginners and advanced developers.


How Node.js is Created 🤔?

Node.js is actually created with two main components:

  1. V8 Engine

  2. LibUV

1. V8 Engine

The V8 Engine, created by Google, is built using both JavaScript and C++. It serves as a core component of Node.js, translating JavaScript code into optimized machine code at runtime. This blend of JavaScript for high-level operations and C++ for low-level system interactions ensures V8's high performance and efficiency

Code Example:

// Demonstrating how JavaScript code is executed by the V8 Enginefunction sum(a, b) {
  return a + b;
}

console.log(sum(10, 5));// Outputs 15Copy

2. LibUV

LibUV is a critical component of Node.js, providing a cross-platform I/O abstraction layer with a focus on asynchronous operations. Its architecture is foundational to Node.js's non-blocking nature, consisting primarily of the Event Loop and a Thread Pool. Below we delve into these components, accompanied by real-life examples, code snippets, and a simplified diagram to illustrate their functions.

1. Event Loop

The Event Loop is at the heart of Node.js's non-blocking I/O operations. It allows Node.js to perform non-I/O heavy operations like network requests, file I/O, or timers asynchronously.

Real-Life Example:

Consider the Event Loop as a restaurant waiter, who takes orders from diners (tasks) and hands them off to the kitchen (system). While the kitchen is preparing the meals, the waiter continues to take more orders, only returning to a table when the kitchen has finished a meal.

Code Example:

// Demonstrating the Event Loop in actionsetImmediate(() => {
  console.log('Immediate execution');
});

setTimeout(() => {
  console.log('Set timeout execution');
}, 0);

process.nextTick(() => {
  console.log('Next tick execution');
});

console.log('Program Started');
Copy

In this example, process.nextTick() gets executed first as it's prioritized by the Event Loop, followed by setTimeout() and setImmediate(), demonstrating the non-blocking nature of the Event Loop.

2. Thread Pool

The Thread Pool, part of LibUV, is used for operations that are too heavy for the Event Loop, such as file system operations or heavy computational tasks. This pool consists of a limited number of threads that handle these operations in the background, allowing the Event Loop to remain free for other tasks.

Real-Life Example:

Imagine a busy office where certain tasks require focused attention and are time-consuming (heavy I/O tasks). These tasks are assigned to a specialized team (Thread Pool) so that the rest of the office (Event Loop) can continue working on other tasks without interruption.

Code Example:

const fs = require('fs');

// Using the Thread Pool for a file system operation
fs.readFile('/path/to/large-file', (err, data) => {
  if (err) throw err;
  console.log('File content:', data.toString());
});

console.log('Reading a large file...');
Copy

Here, fs.readFile() is an I/O operation that utilizes the Thread Pool, allowing the reading of a large file without blocking the Event Loop.

Simplified Diagram:

[ JavaScript Code ] --(sends non-blocking operations)--> [ Event Loop ]
                                                               |
            (for heavy operations)                             |
                    ↓                                         ↓
                [ Thread Pool ] <---(returns results to)--- [ LibUV ]

This diagram represents how Node.js code interfaces with the Event Loop for most operations, and with the Thread Pool, through LibUV, for operations that are too heavy for the Event Loop to handle directly.

Together, the Event Loop and Thread Pool enable Node.js to efficiently manage both lightweight asynchronous tasks and heavier operations, ensuring the environment remains highly performant and scalable.


How The Nodejs Execute the code Internally

Node.js follows a structured process for executing a JavaScript file, such as index.js. Let's dissect the steps involved when you run node index.js in the terminal, focusing on the Node.js process, main thread, module loading, event callbacks, and the Thread Pool. I'll also provide a diagram to illustrate these components and their interactions.

Step-by-Step Process:

  1. Creation of Node Process:

    • When you execute node index.js, Node.js starts a new process. This process has a main thread responsible for executing your JavaScript code.
  2. Initialization:

    • The process initializes the environment, setting up necessary resources and configurations.
  3. Compilation of Top-Level Code:

    • The V8 engine compiles the top-level code in index.js. This includes variable declarations, function definitions, and immediate operations.
  4. Module Resolution:

    • Node.js processes any require() statements, dynamically or locally, from other files. It loads and compiles modules accordingly.
  5. Event Callback Execution:

    • After executing the top-level code, Node.js listens for events (like I/O operations) and executes their callback functions as they occur. This is managed by the Event Loop.
  6. Thread Pool for Intensive Tasks:

    • For CPU-intensive tasks (e.g., I/O operations, cryptography, compression), Node.js uses a Thread Pool. These tasks are offloaded to separate threads in the pool to avoid blocking the main thread.

    • The default size of the Thread Pool was historically 4, but it can be increased up to 1024 in recent versions of Node.js. This allows for parallel processing of heavy tasks.

Diagram:

In this diagram, the flow starts from executing the command in the terminal, leading to the initialization and execution of the script in Node.js. The Thread Pool is highlighted as a crucial component for handling tasks that would otherwise block the main thread, ensuring efficient execution of both I/O-bound and CPU-bound operations.

This structured approach, combining the Event Loop and Thread Pool, allows Node.js to efficiently handle numerous operations, maintaining high performance and scalability, especially in server-side environments.


Understanding Event Loop Phases

The Event Loop is a fundamental concept in Node.js, allowing it to perform non-blocking I/O operations. Despite being single-threaded, Node.js leverages the Event Loop to handle multiple operations concurrently, making it highly efficient for I/O-heavy applications. This article breaks down the workings of the Event Loop, accompanied by a code example, a diagram, and a real-life analogy to provide a comprehensive understanding.

How the Event Loop Works

The Event Loop operates in phases, each responsible for handling different types of tasks. Here's a step-by-step breakdown of these phases:

  1. Expired Timers/Callbacks:

    • Checks for timers whose time thresholds have expired and executes their callbacks.
  2. I/O Polling:

    • Polls for new I/O events, executes their callbacks immediately if available, or schedules them for later execution. This phase handles most I/O operations, including network, file, and database operations.
  3. SetImmediate Callbacks:

    • Executes callbacks scheduled by setImmediate().
  4. Close Callbacks:

    • Executes callbacks for closing requests, like socket.on('close', ...).

If there are no remaining operations to be performed (no timers, I/O callbacks, setImmediate callbacks, or close callbacks), Node.js can exit the event loop. However, if there are pending callbacks, the Event Loop continues to iterate over these phases.

Code Example:

// TimersetTimeout(() => {
  console.log('Timer expired');
}, 1000);

// I/O Operationrequire('fs').readFile('path/to/file', () => {
  console.log('File read completed');
});

// SetImmediatesetImmediate(() => {
  console.log('Immediate callback executed');
});

// Close Eventrequire('net').createServer().close(() => {
  console.log('Server closed');
});

This code snippet demonstrates the execution order influenced by the Event Loop's phases. Although the actual execution order can vary depending on various factors such as file read time and system performance, the conceptual flow remains consistent with the Event Loop's operation phases.

Simplified Diagram:

Real-Life Analogy:

Imagine the Event Loop as a busy coffee shop where the barista (Event Loop) has to manage a series of tasks: preparing online orders placed in advance (expired timers/callbacks), taking new orders (I/O polling), preparing drinks ordered for immediate pickup (setImmediate callbacks), and cleaning used cups (close callbacks). The barista efficiently manages these tasks in a specific order, ensuring that customers are served quickly while also keeping the counter clean. If there are no tasks left, the barista can take a break. However, as new orders come in, the process starts again.

Conclusion

After reading this article, you'll gain a deep understanding of how Node.js operates internally. From the V8 Engine to the Event Loop and Thread Pool, each component is explained with clarity. The real-life examples and code snippets ensure practical insights. By the end, you'll comprehend the step-by-step execution process, empowering you to create efficient and scalable network applications with Node.js.

Resouces For Learning Nodejs

  1. Chai aur Javascript Backend by Hitesh choudhary

  2. Master NodeJS by Piyush Garg

I hope you like this in-depth article on Node.js. Discover how it works internally. If you find this article informative, please feel free to share it with your friends. Also, like this article and follow me for more content like this.