Working with Events in Nodejs

Nodejs is an event-driven runtime environment for Javascript. By this definition, we know the role of Events in Nodejs. Events is one of the build-in modules in Nodejs. Also, other build-in modules depend on the Events.  In this article, we will learn all about Events in Nodejs.

Handling of asynchronous Events

Node use callbacks to handle the asynchronous events. Then Javascript supports Promises and async-await features. A callback is a function that is passed to another function. "Hey call me whenever the data are ready"

Once the Promise were introduced, instead of passing a callback, the method will return a promise. Then we chain .then() for the data and .catch() for the error. While working with the method, we can pass a callback as well to support the use of the method for both Promises and callbacks.

Then there comes the async/await, which make the code look like async code, readable and still works asynchronously. An async function can only await a promise. We need to wrap the code block with try-catch to handle any errors.

The Events Module

Events is one of the build-in modules in Nodejs. As Nodejs is built around the event-driven architecture, the Events module is used by other build-in modules. streams, fs, net to name a few.

Much of the Node.js core API is built around an idiomatic asynchronous event-driven architecture in which certain kinds of objects (called "emitters") emit named events that cause Function objects ("listeners") to be called.

The Event modules let us create custom events in Nodejs. The Event modules have the class EventEmitter, which is used to trigger and handle the event.


const EventEmitter = require('events');

class MyEmitter extends EventEmitter {};

const myEmitter = new MyEmitter();

myEmitter.on('something', () => {
console.log("Something Event is triggered.")
})

myEmitter.emit('something')

In the above code, we require the events module. Then create a class named MyEmitter by extending the EventEmitter class. Then we create an instance of the newly created MyEmitter class. We define a listener for the event 'something', then we emit the event 'something'.

The above code gives the output


Something Event is triggered.

We can define multiple listeners for the same event. The listeners are called synchronously, in the same order, they were registered.


const EventEmitter = require('events');

class MyEmitter extends EventEmitter {};

const myEmitter = new MyEmitter();

myEmitter.on('something', () => {
console.log("The First Listener")
})

myEmitter.on('something', () => {
console.log("The second Listener")
})

myEmitter.emit('something')

The above output of the above code.


The First Listener
The second Listener

The above events and handler did not seem to be helpful, Can we pass the parameters for the method on that listener? As you see in the above code, the emit method accepts a parameter, that is the event name. we can pass any other parameters to that method, which will be passed to the listener.


const EventEmitter = require('events');

class MyEmitter extends EventEmitter {};

const myEmitter = new MyEmitter();

myEmitter.on('sum', (a, b) => {
console.log(`${a} + ${b} = ${a+b}`);
})

myEmitter.emit('sum', 5, 15);

Here we call the emit method with 3 parameters, the event name and the two parameters for the listener. Here is the output of the above.


5 + 15 = 20

The listeners defined with .on methods will be called whenever the event is triggered. With Events, we can define a listener which will be called once at most.


const EventEmitter = require('events');

class MyEmitter extends EventEmitter {};

const myEmitter = new MyEmitter();

myEmitter.once('hey', () => {
console.log("Hello!");
})

myEmitter.emit('hey');
myEmitter.emit('hey');
myEmitter.emit('hey');

The above code outputs the Hello! only once, because we have defined the 'hey' listener using the once.

Working with Events

In the real world application, we need to have the emitter available application-wide, thus we can call the event whenever required. As an example, let's set up a system where we will send a "verify an email" email to users once they register.


const EventEmitter = require('events');

const {
sendVerificationEmail
} = require('./service/account');


class AccountEmitter extends EventEmitter {};

const accountEmitter = new AccountEmitter();

accountEmitter.on('email', sendVerificationEmail)

module.exports = accountEmitter;

We have this module, which will create an emitter, and export it. Then in the controller, when we complete the registration process, we will emit the email event.


const accountEmitter = require('./accountEmitter');

const registerHandeler = (req, res, next) => {
// do all the registration
// get the id
let id = 12;
accountEmitter.emit('email', id);
}

registerHandeler({}, {}, {}); // just to test

And in the account service, we will generate the unique link/code and send mail to the client.


module.exports.sendVerificationEmail = (userId) => {
// send the email
console.log("seding email to userid", userId)
}

Cleaning Up the Listener

We can give any method as a listener to the event. Whenever the event is assigned to a listener, the listener will remain on the memory till the emitter exists. So if we have the scenario where we may need to listen to some events till some other event. we need to clean up the listener thus it can be garbage collected.


const EventEmitter = require('events');

class MyEmitter extends EventEmitter {};

const myEmitter = new MyEmitter();

const aListener = () => {
console.log("Event triggered")
}

myEmitter.on('event', aListener);

myEmitter.emit('event');

myEmitter.removeListener('event', aListener);

myEmitter.emit('event');

Above we introduce a new method called removeListener, which will accept two parameters, first the event name and second the method reference. Since we need the reference of the function, we may want to save the reference of the function.

In the above code, the "Event triggered", will be consoled only once, for the first emit. Next, we removed the listener and when we emit it again, it won't be called. As we can have multiple listeners for the same event, we need to specify, which listener to remove.


myEmitter.removeAllListeners(); // remove all listener
myEmitter.removeAllListeners('event1') // remove all event1 listeners

We can remove all listeners of the event instance using the removeAllListeners() or we can remove all the listeners of an event by providing the event name to removeAllListeners('event');

Events is one of the core modules in Nodejs, various build-in modules use Events. The Event modules let us create custom events in Nodejs. The Event modules have the class EventEmitter, which is used to trigger and handle the event.

Posted by Sagar Devkota

0 Comments