The reactor pattern is the heart of Node.js. It is a specialisation of the previously discussed event demultiplexer algorithm. libuv, the non-blocking I/O engine of Node.js, implements the reactor pattern.
So, how does it work?
Let’s say there’s a single threaded application interested in reading data from a file (an I/O operation). The application kicks off this process by,
- Submitting a request to the event demultiplexer. This request consists of three components,
- A resource (File).
- An operation on the resource that the event demultiplexer must perform (Read).
- A handler that the demultiplexer can call with the results of the operation (
processData
).
demultiplexer.watch(file, 'FOR_READ', processData);
This function call is non-blocking, and control (dashed lines) immediately returns to the application. The application thread is hence not blocked, and it is free to execute other instructions.
In Node, this looks like,
const fs = require('fs');
/**
* This is the handler that gets passed to the event demultiplexer.
*/
function processData(data) {
console.log(data);
}
/**
* Register with the demultiplexer. This call is non-blocking and returns immediately
*/
fs.readFile('path', processData);
console.log('Other instructions') // Application is free to execute other instructions
The demultiplexer runs in a separate thread(s). Its instructions are not run by the application thread. In the demultiplexer thread,
- When a set of I/O operations complete, the demultiplexer pushes new events onto the Event Queue.
- Then, when a new set of events are available in the queue, the event loop iterates over each event.
- For each event, the associated handler is called.
- The handler code is part of the application and, so, runs on the application thread. Here, two things can happen,
- Application executes the handler and gives back control to the event loop, or,
- The handler requests more I/O operations by registering them with the demultiplexer and then gives back control to the event loop
- When all the items from the Event Queue are processed, the loop will block again on the Event Demultiplexer, which will then trigger another cycle.
The asynchronous behaviour is now clear. The application expresses an interest to access a resource at one point in time (without blocking) and provides a handler which will then be invoked at a later point in time by the demultiplexer.
Pattern – Reactor
It reacts to events.
When does a Node.js application exit?
Is Node.js single threaded?
Most good programmers do programming not because they expect to get paid or get adulation by the public, but because it is fun to program.
Linus Torvalds
Very good
Nice write up