Node's primary organizational principle is eliminating blocking processes through event-driven, asynchronous I/O.
If a process's start, stop, and idle state are understood as events that can be subscribed to and acted upon, we can begin to discuss how extremely complex systems can be constructed within this new and, at heart, quite simple to grasp model.
What would an environment within which many clients’ jobs are cooperatively scheduled to look like? And how is this message passing between events handled? Let’s explore two popular options and discover the one that greatly influences Node Architectural Design choice.
Collaboration
Workers could be assigned new tasks in a collaborative environment instead of idling. A virtual switchboard called a dispatcher is used.
Queueing
We might add a buffer (queue) between the clients and the dispatcher(Libuv) to avoid overwhelming anyone. This is responsible for managing the prioritized queue of client tasks, sending and receiving requests and responses from the dispatcher and worker when idle and when not.
The queue model greatly inspires the Node Architectural Design choice. However, to implement the queueing model, the Node system is made up of three components:
V8 engine
LibUv
Node (Service Manager)
V8 Engine
V8 is Google’s JavaScript engine, written in C++. It compiles and executes JavaScript code inside a VM (Virtual Machine). The Node event loop is ultimately doing the method of building up and tearing down a single stack.
LibUv
Node.js internals uses the Libuv library to enforce an asynchronous, event-driven programming style. The core job of Libuv in Node.js is to provide an event loop and callback-based notifications of I/O and other activities.
It is a multi-platform support library focusing on Asynchronous I/O and other core utilities like timers, non-blocking networking support, asynchronous file system access, child processes, and more in Node.js.
Speaking of Event Loops, Node.js uses event-driven programming architecture. A program indicates interest in certain events and responds to them when these events occur. Libuv handles gathering these events from the operating system and monitoring other sources of events. Since the event loop runs forever, users can register callbacks to be invoked when an event occurs.
Here’s a pseudocode explaining how to event loop works:
while there are still events to process:
e = get the next event
if there is a callback associated with e:
call the callback
Node.js event loop is a broad topic, and I will create detailed content exploring the Event loop here. However, this video from ProgrammingWithMosh is a good one.
Node (Service Manager)
Node is the services manager running through the provided instructions and prioritizing them. When a potentially blocking task is encountered (I/O, timers, and streams), it is handed over to the dispatcher (the libuv thread pool). Otherwise, the instruction is queued up for the event loop to pop and execute.
The primary idea of Node.js is that all I/O or time-consuming operations occupy the worker's pools which the Node service manager will hand over to the dispatcher (the Libuv thread pool) while the remaining work (Non-I/O) tasks are sent to the single thread of V8.
The Node engine is the services manager running infinitely through the provided instructions, prioritizing them, and returning all responses via callback to the client.