The reactor pattern

The Reactor pattern in Node.js

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,

The Reactor pattern in Node.js
The Reactor pattern
  1. 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,

  1. When a set of I/O operations complete, the demultiplexer pushes new events onto the Event Queue.
  2. Then, when a new set of events are available in the queue, the event loop iterates over each event.
  3. For each event, the associated handler is called.
  4. The handler code is part of the application and, so, runs on the application thread. Here, two things can happen,
    1. Application executes the handler and gives back control to the event loop, or,
    2. The handler requests more I/O operations by registering them with the demultiplexer and then gives back control to the event loop
  5. 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

Handles I/O operations by blocking until new events are available from a set of observed resources, and then reacting by dispatching each event to an associated event handler.
It reacts to events.

Nodejs design patterns book cover
This is the only Node.js book you’ll ever need.
Click to buy now!

When does a Node.js application exit?

A Node.js application exits automatically when there are no more pending operations in the Event Demultiplexer and no more events to be processed in the Event Queue.

Is Node.js single threaded?

Not really. The application thread (all the JavaScript code you write) runs in a single thread, but underlying components, like libuv, run in different threads.


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

0 0 votes
Article Rating
guest
2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Anonymous
Anonymous
10 months ago

Very good

Anonymous
Anonymous
10 months ago

Nice write up