Node.js Event Loop
Node.js is a single-process, single-threaded application, but due to the asynchronous execution callback interface provided by the V8 engine, it can handle a large number of concurrent operations, resulting in very high performance.
Almost every API in Node.js supports callback functions.
Node.js implements its event mechanism using the Observer pattern from design patterns.
Node.js operates in a single thread similar to an infinite while(true) event loop, until there are no more event observers to exit. Each asynchronous event generates an event observer, and if an event occurs, the corresponding callback function is called.
Event-Driven Programming
Node.js uses an event-driven model where the web server receives a request, closes it for processing, and then serves the next web request.
When the request is completed, it is placed back into the processing queue. When it reaches the front of the queue, the result is returned to the user.
This model is highly efficient and scalable because the webserver continuously accepts requests without waiting for any read or write operations. (This is also known as non-blocking I/O or event-driven I/O.)
The entire event-driven process is implemented in a very straightforward manner, somewhat akin to the Observer pattern, where the event acts as a subject, and all registered handler functions act as observers.
Node.js has several built-in events. We can bind and listen to events by importing the events module and instantiating the EventEmitter class, as shown in the following example:
// Import the events module
var events = require('events');
// Create an eventEmitter object
var eventEmitter = new events.EventEmitter();
The following program binds an event handler:
// Bind the event with the handler
eventEmitter.on('eventName', eventHandler);
We can trigger the event with the following code:
// Trigger the event
eventEmitter.emit('eventName');
Example
Create a file named main.js with the following code:
// Import the events module
var events = require('events');
// Create an eventEmitter object
var eventEmitter = new events.EventEmitter();
// Create an event handler
var connectHandler = function connected() {
console.log('Connection successful.');
// Trigger the data_received event
eventEmitter.emit('data_received');
}
// Bind the connection event with the handler
eventEmitter.on('connection', connectHandler);
// Bind the data_received event with an anonymous function
eventEmitter.on('data_received', function(){
console.log('Data received successfully.');
});
// Trigger the connection event
eventEmitter.emit('connection');
console.log("Program execution complete.");
Execute the above code as follows:
$ node main.js
Connection successful.
Data received successfully.
Program execution complete.
How Does a Node Application Work?
In a Node application, functions that perform asynchronous operations take a callback function as the last parameter, and this callback function receives an error object as the first parameter.
Let's revisit the previous example. Create an input.txt file with the following content:
tutorialpro.org official website: www.tutorialpro.org
Create a main.js file with the following code:
var fs = require("fs");
fs.readFile('input.txt', function (err, data) {
if (err){
console.log(err.stack);
return;
}
console.log(data.toString());
});
console.log("Program execution complete");
In the above program, fs.readFile() is an asynchronous function used to read files. If an error occurs during file reading, the error object err will output the error message.
If no error occurs, readFile skips the output of the err object, and the file content is output through the callback function.
Executing the above code results in the following output:
Program execution complete
tutorialpro.org official website: www.tutorialpro.org
If we delete the input.txt file and run the code, the output will be as follows:
Program execution complete
Error: ENOENT, open 'input.txt'
The error message indicates that the file 'input.txt' does not exist.