Emitters


In Node.js, objects that can emit events are called Emitters. Emitters emit named events that cause Function objects (event handlers) to be called. Typically, event names are camel-cased strings.


All Emitters are instances of the EventEmitter class or of a subclass of the EventEmitter class. The EventEmitter class is defined in the events module. Note that the module name does not match the class name.


1 | Methods available to Emitters


Every Emitter inherit the properties from the EventEmitter.prototype object. We can get the list of these properties with the following code:


// Example 1
const EventEmitter = require('events');

console.log(EventEmitter.prototype);

First, we import the Node.js core module events, and assign it to the variable EventEmitter.


Then, we log all properties of the EventEmitter.prototype object. Among these properties, we will explore 3 methods:


  • 1 method to emit our own events. This method is rarely used in Node.js, since we usually use built-in Node.js Emitters that emit their own predefined events. Most of the time, we will be calling event handlers rather than creating Emitters. But we need this method here to demonstrate the use of the following methods.
  • 2 methods to register event handlers.

It is fundamental to know the 2 methods to register event handlers. It is a very common thing to do in Node.js.


2 | EventEmitter.prototype.emit()


2.1 | Characteristics


The method EventEmitter.prototype.emit() takes 2 or more arguments:


  1. The first argument, eventName, is the name of the event (a String) we want to emit.
  2. The following arguments, ...args, are meant to be passed to the event handlers if there is any.

The method returns true if the event has at least one event handler, and false otherwise.


2.2 | Example


// Example 2
const EventEmitter = require('events');

const specialEmitter_2 = new EventEmitter();

specialEmitter_2.emit('test', 'Sample data');

First, we import the Node.js core module events, and assign it to the variable EventEmitter.


Then, we create an instance of the EventEmitter class by using the new keyword with the constructor function EventEmitter(). The Emitter we create is assigned to the variable specialEmitter_2.


Finally, we invoke the method EventEmitter.prototype.emit() on the Emitter specialEmitter_2 with 2 arguments:


  1. The argument 'test': the String 'test' is the name of the event.
  2. The argument 'Sample data': this String would be passed to any event handler called for the particular event 'test', if it were the case. Since there is no event handler, there no way to "handle" the 'test' event that is emitted by specialEmitter_2.

Also, the return value of the last statement is false, since there is no event handler associated with the 'test' event.


Emitting events without having event handlers is useless, so let us see how to register event handlers.


3 | EventEmitter.prototype.on()


3.1 | Characteristics


EventEmitter.prototype.addListener() is an alias of EventEmitter.prototype.on(). Both methods are interchangeable. They are methods that let us call event handlers for specific events. They take 2 arguments:


  1. The 1st argument, eventName, is the name of the event (a String) to listen to.
  2. The 2nd argument, listener, is the callback function to invoke when the event specified by the 1st argument is emitted.

These methods return the calling Emitter. So it is possible to chain multiple methods one after the other, if need be.


3.2 | Example


// Example 3
const EventEmitter = require('events');

const specialEmitter_3 = new EventEmitter();

specialEmitter_3.on('test', (data) => console.log(`Test emitted: ${data}`));

specialEmitter_3.emit('test', 'Sample data 1');
specialEmitter_3.emit('test', 'Sample data 2');
specialEmitter_3.emit('test', 'Sample data 3');

In the previous code, we invoke the method EventEmitter.prototype.on() on the Emitter specialEmitter_3. We pass 2 arguments to the method:


  1. The first argument is the name of the event we want to listen to. In our case, it is the event 'test'.
  2. The 2nd argument is the function that will be called when and if the event 'test' is emitted. This function takes in the data argument passed by the Emitter when emitting the event 'test', and logs it to the screen with the String 'Test emitted:'.

The method returns the Emitter specialEmitter_3.


Then, we make the Emitter specialEmitter_3 emit the event 'test' 3 times, with 3 different Strings to represent 3 different pieces of data. For each call of the method EventEmitter.prototype.emit() on specialEmitter_3, the event handler is called. The previous code displays to the screen:


'Test emitted: Sample data 1'
'Test emitted: Sample data 2'
'Test emitted: Sample data 3'

Also, note that the method EventEmitter.prototype.emit() is a blocking function while the method EventEmitter.prototype.on() is a non-blocking function.


3.3 | Example


// Example 4
const EventEmitter = require('events');

const specialEmitter_4 = new EventEmitter();

specialEmitter_4
  .on('test', (data) => console.log(`Test emitted: 1st event handler`))
	.on('test', (data) => console.log(`Test emitted: 2nd event handler`))
	.on('test', (data) => console.log(`Test emitted: 3rd event handler`));

specialEmitter_4.emit('test');

In this code, we chain 3 calls of the method EventEmitter.prototype.on(). Since, this method always returns the Emitter it is called on, we are calling 3 event handlers on the same Emitter specialEmitter_4.


It is possible to call several event handlers for the same event. But, by default, Emitters will print a warning if more than 10 listeners are added for a particular event. This is a useful default that helps finding memory leaks. The emitter.setMaxListeners() method allows the limit to be modified for a specific Emitter. The value can be set to Infinity (or 0) to indicate an unlimited number of listeners.


Also, note that when the Emitter specialEmitter_4 emits the event 'test', the event handlers are called one after the other, in the exact order in which they are invoked (synchronously). That is why the previous code will always display to the screen:


'Test emitted: 1st event handler'
'Test emitted: 2nd event handler'
'Test emitted: 3rd event handler'

4 | EventEmitter.prototype.once()


4.1 | Characteristics


The method EventEmitter.prototype.once() adds a one-time listener function for the event named eventName. The next time eventName is emitted, the listener is removed and then invoked. This method takes 2 arguments:


  1. The 1st argument, eventName, is the name of the event (a String) to listen to.
  2. The 2dn argument, listener, is the callback function to invoke when the event specified by the 1st argument is emitted.

These methods return the calling Emitter. So it is possible to chain multiple methods one after the other, if need be.


4.2 | Example


// Example 5
const EventEmitter = require('events');

const specialEmitter_5 = new EventEmitter();

specialEmitter_5.once('test', (data) => console.log(`Test emitted: ${data}`));

specialEmitter_5.emit('test', 'Sample data 1');
specialEmitter_5.emit('test', 'Sample data 2');
specialEmitter_5.emit('test', 'Sample data 3');

When we call the method EventEmitter.prototype.once() on the Emitter specialEmitter_5, we pass 2 arguments to it:


  1. The first argument is the name of the event to listen to. In this case, it is the event 'test'.
  2. The 2nd argument is the function that will be called when and if the event 'test' is emitted. This function takes in the data argument passed by the Emitter when emitting the event 'test', and logs it to the screen with the string 'Test emitted:'.

The return value of the method is the Emitter referenced by specialEmitter_5.


Since we are using the method EventEmitter.prototype.once(), the method will be able to listen to only one "emission" of the event 'test'. That is why the previous code only longs to the screen 'Test emitted: Sample data 1'. After the first call of the method EventEmitter.prototype.emit(), the event handler is unregistered: it stops listening to events.


Author:
Initial publication date:
Last updated: