Event Emitters in Node.js

On the frontend-side of an application the user interactions are handled through events, click events, keyboard events, mouse moving events, etc. In Node.js (backend-side) we can build a similar system using the events module.

What is the Event Emitter?

Much of the Node.js core API is built around event-driven architecture. There objects or emitters emit events that cause a function object or listener to be called. For example, HTTP and TCP servers are an event emitter, a TCP socket is an event emitter, HTTP request and response objects are event emitters.

All objects that emit events are instances of the EventEmitter class and expose an eventEmitter.on() function so functions can be attached to the named events. Event names are typically camel-cased.

Creating an Event Emitter

The events module exports an EventEmitter constructor, depending on the Node version the events module is the EventEmitter or you have to deconstruct it.

const EventEmitter = require('events');

The EventEmitter is a constructor and to create a new event emitter the constructor has to be called with the new keyword.

const myEmitter = new EventEmitter();

A common usage pattern with EventEmitteris inheritance. Look at this article for a refresher on Inheritance in Javascript.

class MyEmitter extends EventEmitter {
  constructor(opts = {}) {
    super(opts);
    this.name = opts.name;
  }
}

Emitting Events

The created EventEmitter object exposes two methods - on and emit.

  • emit is used to trigger an event.
  • on is used to add a callback function.

To emit an event the emit method has to be called:

const EventEmitter = require('events');
const myEmitter = new EventEmitter();
myEmitter.emit('event');

The first argument of emit is the event namespace. We need to know the event namespace to be able to listen to emitted events.

The eventEmitter.emit() method allows an arbitrary set of arguments to be passed to the listener functions, like myEmitter.emit('event', 'a', 'b');

The following example emits an error event and a close event when the method destroy is called with an error.

const EventEmitter = require('events')
class MyEmitter extends EventEmitter {
  constructor (opts = {}) {
    super(opts);
    this.name = opts.name;
  },
  destroy (err) {
    if (err) { this.emit('error', err) }
    this.emit('close');
  }
}

Listening for Events

To listen to an emitted event the event emitter object has a method addListener or the alias on.

Copy the following code in the Node REPL and execute.

const EventEmitter = require('events');

const eventEmitter = new EventEmitter();
eventEmitter.on('close', () => {
  console.log('close event fired!');
});
eventEmitter.emit('close');

The output will be close event fired!.

Arguments passed to emit are received by the listener.

For example:

const EventEmitter = require('events');

const eventEmitter = new EventEmitter();
eventEmitter.on('add', (a, b) => {
  console.log('add event fired!');
  console.log(a + b);
});
eventEmitter.emit('add', 1, 2);

Ordering is important. Event listeners will not react to events if the event is emitted before the listener is added. Event listeners are also called in the order they are registered.

const EventEmitter = require('events');
const eventEmitter = new EventEmitter();
eventEmitter.on('my-event', () => {
  console.log('1st');
});
eventEmitter.on('my-event', () => {
  console.log('2nd');
});
eventEmitter.emit('my-event');

The output from the code above will be 1st and then 2nd.

There is a method which can be used to inject listeners to the top prependListener.

const EventEmitter = require('events');
const eventEmitter = new EventEmitter();
eventEmitter.on('my-event', () => {
  console.log('1st');
});
eventEmitter.prependListener('my-event', () => {
  console.log('2nd');
});
eventEmitter.emit('my-event');

Now the output will be 2nd and then 1st.

Single Use Listener

An event can also be emitted more than once. To reduce this the once method can be used. It removes its listener after it has been called.

const EventEmitter = require('events');
const eventEmitter = new EventEmitter();
eventEmitter.once('event', () => {
  console.log('event fired');
});
eventEmitter.emit('event');
eventEmitter.emit('event');

The output event fired will only be logged once.

Removing Listeners

Listeners can be removed with the removeListener method. It takes two arguments the event name, and the listener function.

const callback = stream => {
  console.log('someone connected!');
};
server.on('connection', callback);
// ...
server.removeListener('connection', callback);

removeListener() will remove, at most, one instance of a listener from the listener array. If a single listener has been added multiple times for the specified eventName, then removeListener() must be called multiple times to remove each instance.

The removeAllListeners method can be used to remove listeners without having a reference to their function. It takes one optional argument, the event name. It is useful if multiple listener for the same event name have been registered.

Emitting an error event on an event emitter will cause the event emitter to throw an exception if a listener for the error event has not been registered.

TL;DR

  • The Node.js core API is built around event-driven architecture.
  • The eventEmitter is a constructor method.
  • To emit events the emit method has to be used with an event name as an argument.
  • To listen to events the alias on or addEventListener can be used with the event name.
  • The event name should be camelCased.

Thanks for reading and if you have any questions , use the comment function or send me a message @mariokandut.

If you want to know more about Node, have a look at these Node Tutorials.

References (and Big thanks):

30