Event emitters are objects in NodeJS that trigger events by sending a message to signal that an action has occurred.

Node.js provides a built-in events module. It contains an event emitter class that lets you create and handle custom events via callback functions.

Here you will learn how to emit events, listen and handle event data, and handle event errors in NodeJS.

Emitting Events

The events module is a core part of the Node.js server-side environment. Thus, you don't need to install it, but before using the EventEmitter class, you must import from the events module and instantiate it.

Like so:

        const EventEmitter = require("events");
 
// Instantiatating the EventEmitter
const myEmitter = new EventEmitter();

You can emit events using the EventEmitter’s emit method. The emit method takes an eventName and an arbitrary number of arguments as parameters.

Once you call the emit method, it emits the passed eventName. Then it synchronously calls each of the event's listeners in the order you registered them, passing the supplied arguments to each. Finally, it returns true if the event had listeners and false if it didn't have any listeners.

For example:

        myEmitter.emit("TestEvent", "foo", "bar", 1, 2);

In the code block above, you passed TestEvent as the EventName, and "foo,” "bar,” 1, and 2 as the arguments. When the code block above runs it will notify all listeners listening for the TestEvent event. It will call those listeners with the given arguments.

Listening for Events

You can listen for emitted events using the EventEmitter’s on method. The on method takes an EventName and a callback function as parameters. When the event with the EventName passed into the on method is emitted, it invokes its callback function. This method returns a reference to the EventEmitter, allowing you to chain multiple calls.

For example:

        // First Listener
myEmitter.on("TestEvent", () => {
  console.log("TestEvent Emitted!!!");
}); // TestEvent Emitted!!!
 
// Second Listener
myEmitter.on("TestEvent", (...args) => {
  args = args.join(", ");
  console.log(`Event emitted with the following arguments: ${args}`);
}); // Event emitted with the following arguments: foo, bar, 1, 2
 
myEmitter.emit("TestEvent", "foo", "bar", 1, 2);

In the code block above, when the TestEvent event emits, the listeners for the event will invoke their callback functions. The listeners will react in the order you registered them, meaning that the “first listener’s” callback will run before the second, and so on.

You can change this behavior using the EventEmitter’s prependListener method. This method takes the same parameters as the on method. The difference is that this method reacts to the event first, regardless of the time you register it.

For example:

        myEmitter.on("TestEvent", () => {
  console.log("TestEvent Emitted!!!");
});
 
myEmitter.prependListener("TestEvent", () => {
    console.log("Executes first")
})
 
// console.log(myEmitter.listeners("TestEvent"));
myEmitter.emit("TestEvent", "foo", "bar", 1, 2);

When the code block above executes, “Executes first” will be logged first to the console, followed by “TestEvent Emitted!!!” irrespective of the order you registered them in because of the prependListener method.

If you register several listeners with the prependListener method, they will run in order from the last to the first.

Note the arrangement of the emitter and listeners. The listeners always come before the emitter. This arrangement is because the listeners must already be listening for the event before the emitter emits it.

For context, consider the code block below:

        myEmitter.emit("TestEvent", "foo", "bar", 1, 2);
 
myEmitter.on("TestEvent", () => {
  console.log("TestEvent Emitted!!!");
});

If you run the code block above, nothing happens because, at the time the emitter emitted the event, no listener was listening for the event.

Listening for Events Once

Depending on your requirements, you may need to handle some events only once in your application’s lifecycle. You can achieve this using the EventEmitter’s once method.

This method takes the same arguments as the on method and works similarly. The only difference is that the listeners registered with the once method only listen for the event once.

For example:

        myEmitter.once("SingleEvent", () => {
  console.log("Event handled once");
});
 
myEmitter.emit("SingleEvent"); // Event handled once
myEmitter.emit("SingleEvent"); // Ignored
myEmitter.emit("SingleEvent"); // Ignored

Running the code block will only log “Event handled once” to the console once, regardless of how often the emitter emits the event.

Listeners registered with the once method react to the event in the order you register them. You can change this behavior using the prependOnceListener method, which works like prependListener. The only difference is that the listeners registered with the once method only listen for the event once.

Handling Errors With Event Emitters

You should take care to handle JavaScript errors appropriately and event listeners are no exception. Unhandled errors from them will cause the Node.js process to exit and your application to crash.

To handle an error event, at least one of the event’s listeners must have its EventName set to error.

For example:

        myEmitter.on("error", (error) => {
  console.error(`Error: ${error}`);
});

Having a listener handle a potential error, like in the code block above, will stop the application from crashing when an error occurs.

For example:

        myEmitter.emit("error", new Error("This is an error"));

Running the code block above will log “This is an error” to the console because a listener is handling error events.

Managing Event Listeners

The EventEmitter class has several methods that allow you to manipulate and manage event listeners. You can get an event’s listeners, remove them, and set the maximum number of listeners for an event.

Here is a table containing EventEmitter methods you can manipulate Event listeners with:

Method

Arguments

Return Value

listenerCount

eventName

Returns the number of listeners subscribed to an event

listeners

eventName

Returns an array of listeners

removeListener

eventName

Removes at least one listener from a specified eventName.

removeAllListeners

eventName

Removes all listeners for a specified eventName. If you don’t specify an event name, this method call will remove all listeners for the EventEmitter.

setMaxListeners

number

Changes the default max number of listeners per event. Use infinity or zero to indicate an unlimited number of listeners. By default, you can only subscribe ten listeners to an event.

You can only call these methods on an EventEmitter instance.

For example:

        myEmitter.removeListener("TestEvent");

The code block above removes a single listener for the TestEvent event.

The Importance of Event Emitters

Node.js adopts the event-driven programming paradigm with its support for event emitters and listeners. Event-driven programming is one of the reasons Node.js programs are faster and more straightforward than some alternatives. You can easily synchronize multiple events, resulting in improved efficiency.