What are events in node JS?

Read it in 8 Mins

Last updated on
08th Dec, 2021
Published
07th Dec, 2021
Views
6,592
What are events in node JS?

Node.js for the most part is based on an asynchronous event-driven architecture pattern. This means that many of the core APIs in Node work in a reactive fashion, where an emitter (a type of object) will emit a named event that will trigger the callback functions attached through listener objects for that specific named event.

Let’s consider the following example of reading data from a file in a memory-efficient way using Node.js and streams:

const fs = require('fs');
 
const dataStream = fs.createReadStream('data.txt', 'utf-8');
 
dataStream.on('readable', () => {
console.log(dataStream.read());
});

dataStream.on('end', () => console.log('File Read Completed'));

What are events in node JS?

In the above code, the object dataStream, created by `fs.createReadStream` is an emitter that emits various named events when those specific events occur.

For instance, when the file contents are serialized and are ready to be read, a `readable` event will be emitted. We have attached a listener to this particular event emitted by the dataStream object using the `on` method.

Now, when the `readable` event is emitted, the callback function attached to its listener that logs the content of the file will be executed. Thus, the entire operation is done in an asynchronous, event-driven way.

When the stream reaches the end of the file, the `end` event is emitted and the corresponding callback attached to its listener will be triggered.

What we have seen above is a typical example of how the core APIs of Node.js is built around events. In this article, we will briefly discuss how to create custom events and the various ways to listen to the emitted events.

The EventEmitter:

We have seen in the above section that for objects to emit events, they need to be of type emitter. Emitters in a nutshell are object instances created from either the EventEmitter class or another class that inherits the EventEmiiter class.

In Node.js the EventEmitter class is exported by the events module.

The EventEmitter class encapsulates a method named `emit` which can be used to emit a named event from the created object instance.

To attach listeners, we can make use of the `on` method which will be available on the object’s prototype on which we triggered an emit.

Let’s have a quick look at an example code snippet to demonstrate the concepts we have discussed above:

const EventEmitter = require('events');
const customEmitter = new EventEmitter();
customEmitter.on('exampleEvent', (data) => console.log(data));
customEmitter.emit('exampleEvent', 'A custom event was triggered!!');

What are events in node JS?
In the above example, we have created an emitter instance from the EventEmitter class and utilized the `emit` method on the instantiated object to trigger a named event called `exampleEvent` that passes a custom string as data.

We have also utilized the `on` method of the object to listen to this custom event and have attached a callback function which just logs the data passed from the emitted event on the console.

Thus, we have created our own custom event. It is also important to note that although the entire event mechanism is asynchronous, if we attach more than one listener to the same event on the same emitter, the listener callbacks will be executed in the order they were declared in the code in a synchronous fashion.

The emit Method:

The `emit` method has the following syntax:

emitter.emit(eventName,[,...args])

The first parameter is the name of the event that we would like to emit, followed by any number of parameters.

This means that we could do something like the following, with the emit method:

customEmitter.emit("newLine", "A custom newLine event has been emitted", true);

The emit method returns true if one or more listeners are attached. If there are no listeners attached for a particular event, then the emit method returns false.

The on Method:

The `on` method is used to attach listeners for the named event of an emitter object.

The generic syntax is as follows:

emitter.on(eventName,listener)

The eventName is the string identifying the named event while the listener is the callback function.

The `on` method returns the EventEmitter instance to enable method chaining.

Thus, we could write our listener in the following way:

customEmitter.on("newLine", (arg1,arg2)=>{ 
    console.log(arg1); 
    console.log(arg2); 
});

What are events in node JS?
It is also important to note that Node doesn’t perform any check if a particular listener is already attached for an emitter. This means that if we attach the same listeners multiple times for a particular event from an emitter, the listeners’ callbacks will be run as many times as we attached them.

By default, listeners will be appended to the listener array for an event from an emitter. But if we would like a listener callback to be executed before all the other attached listeners, then we can make use of the `prependListener` method on the emitter object.

Listeners attached using the `on` method will be invoked every time the particular event is emitted. For scenarios where we need the listener to be invoked only once, we could make use of a different method to attach listeners, which we’ll discuss in the following section.

Handling Events Only Once:

In order to handle events just one time, we can use the `once` method. It has a signature that is similar to the `on` method:

emitter.once(eventName, listener)

Let’s look at a demo code snippet:

const EventEmitter = require('events');
const customEmitter = new EventEmitter();
customEmitter.once('event1', (data) => console.log(data));
customEmitter.emit('event1', 'event 1 emitted'); 
customEmitter.emit('event1', 'event 1 emitted');

In the above code, the named event `event1` is emitted twice. But the listener will be invoked only once.

What are events in node JS?
If we had used `on` instead, the listener would have been invoked twice as follows:

What are events in node JS?

Error Handling:

In Node.js, most of the core API follow a pattern called “Error-first callbacks”, which means that if there were any errors, those should be the first argument for the callback function.

A generic snippet to represent the idea could be as follows:

fs.readFile('data.csv', function(err, data) { 
if (err) { 
console.log('Error Found:' + err); 
throw err; 
} 
console.log(data); 
});

But with event emitters, we follow a slightly different approach to handling errors. Since the emitters can emit custom events, an error event could be emitted when we encounter one and we could attach a listener with a callback that will act as the error handler.

This idea is demonstrated by the following snippet:

const EventEmitter = require('events');
const customEmitter = new EventEmitter(); 
  
const sporadicError = () => { 
const diceRoll = Math.random(); 
if (diceRoll >= 0.6) { 
   throw new Error('An error occurred in the application'); 
} 
}; 
  
customEmitter.on('data', (data) => console.log(data)); 
customEmitter.on('error', (error) => console.log(error)); 
  
try { 
sporadicError(); 
customEmitter.emit('data', 'That was successful'); 
} catch (err) { 
customEmitter.emit('error', err.message); 
}

The above code simulates a random error occurrence scenario where the error is sporadic and has an occurrence probability of 40%.

The output of the above snippet on two consecutive runs was as follows:

What are events in node JS?
Through this code, we have demonstrated how error handling could be done in event emitters.

Are Events really important?

Events are a crucial mechanism in the Node.js paradigm. Having a clear understanding of the event-driven approach will help one to write idiomatic Node code and will also, to a great extent, prevent us from writing blocking synchronous logic. If this article has kindled your curiosity, then you should also have a quick read about a concept called reactive programming. This will help you to get a deeper understanding of terms such as streams and observables that will, in turn, help one write well-architected, event-driven and resilient application logic.

Profile

Parthipan Natkunam

Author

Parthipan is a full-stack capable, software engineer with 7 years of industry experience, specialized in front-end engineering. Certified "AWS Developer Associate", who has worked in industries such as Ed-tech, IoT and Cyber security with companies spanning across multiple countries with varied tech stacks. Language agnostic by practice but fluent in TypeScript and JavaScript. Loves open-source and enjoys creating quality software products from scratch.