Node.js Events and EventEmitter
Node.js is built on an event-driven architecture. At its core, this means that many operations in Node.js — like reading a file, receiving an HTTP request, or connecting to a database — are triggered by events. The program does not constantly check if something happened; instead, it listens and reacts when an event occurs.
The events module, and specifically the EventEmitter class it provides, is the foundation of this event system. Almost every Node.js core module — including fs, http, and stream — is built on top of EventEmitter.
Understanding Events – A Simple Analogy
Think of a doorbell. The doorbell does not constantly check if someone is there — it simply waits. When someone presses the button, an event is triggered, and the bell rings. The person inside the house hears it and responds.
In Node.js: something emits (fires) an event, and a listener (a function registered to react to that event) responds when the event occurs.
The events Module and EventEmitter
The EventEmitter class is provided by the built-in events module:
const EventEmitter = require('events');
An instance of EventEmitter can register listeners for named events, and emit (trigger) those events whenever needed.
Creating an EventEmitter and Registering a Listener
const EventEmitter = require('events');
// Create an instance of EventEmitter
const myEmitter = new EventEmitter();
// Register a listener for the 'greet' event
myEmitter.on('greet', function() {
console.log("Hello! Someone greeted!");
});
// Emit the 'greet' event
myEmitter.emit('greet');
Output:
Hello! Someone greeted!
.on(eventName, listener) registers a listener function. .emit(eventName) triggers the event and runs all listeners attached to it.
Passing Arguments to Event Listeners
Data can be passed along when an event is emitted:
const EventEmitter = require('events');
const myEmitter = new EventEmitter();
myEmitter.on('userLoggedIn', function(username, role) {
console.log(username + " has logged in as " + role);
});
myEmitter.emit('userLoggedIn', 'Alice', 'admin');
myEmitter.emit('userLoggedIn', 'Bob', 'viewer');
Output:
Alice has logged in as admin
Bob has logged in as viewer
Multiple Listeners on the Same Event
Multiple listeners can be attached to the same event. All of them will be called when the event is emitted:
const EventEmitter = require('events');
const myEmitter = new EventEmitter();
myEmitter.on('start', function() {
console.log("Listener 1: Application started.");
});
myEmitter.on('start', function() {
console.log("Listener 2: Logger started.");
});
myEmitter.on('start', function() {
console.log("Listener 3: Database connected.");
});
myEmitter.emit('start');
Output:
Listener 1: Application started.
Listener 2: Logger started.
Listener 3: Database connected.
One-Time Listeners – once()
The .once() method registers a listener that fires only the first time the event is emitted. After that, it is automatically removed:
const EventEmitter = require('events');
const myEmitter = new EventEmitter();
myEmitter.once('connect', function() {
console.log("Connected to server! (this message appears only once)");
});
myEmitter.emit('connect'); // Fires the listener
myEmitter.emit('connect'); // Listener is gone — nothing happens
Output:
Connected to server! (this message appears only once)
Removing Listeners
A specific listener can be removed using .removeListener() or the alias .off():
const EventEmitter = require('events');
const myEmitter = new EventEmitter();
function onPing() {
console.log("Pong!");
}
myEmitter.on('ping', onPing);
myEmitter.emit('ping'); // Output: Pong!
myEmitter.off('ping', onPing); // Remove the listener
myEmitter.emit('ping'); // Nothing happens now
To remove all listeners for a specific event:
myEmitter.removeAllListeners('ping');
Listing Event Names and Listener Count
const EventEmitter = require('events');
const emitter = new EventEmitter();
emitter.on('data', () => {});
emitter.on('data', () => {});
emitter.on('error', () => {});
console.log(emitter.eventNames()); // ['data', 'error']
console.log(emitter.listenerCount('data')); // 2
Extending EventEmitter – Custom Class
In real-world applications, classes are extended from EventEmitter to add event capabilities to custom objects. This is how built-in Node.js modules like fs and http work internally:
const EventEmitter = require('events');
class OrderSystem extends EventEmitter {
placeOrder(item) {
console.log("Order placed for: " + item);
this.emit('orderPlaced', item);
}
}
const shop = new OrderSystem();
shop.on('orderPlaced', function(item) {
console.log("Processing payment for: " + item);
});
shop.on('orderPlaced', function(item) {
console.log("Sending confirmation email for: " + item);
});
shop.placeOrder("Laptop");
Output:
Order placed for: Laptop
Processing payment for: Laptop
Sending confirmation email for: Laptop
The error Event
Node.js treats the 'error' event specially. If an 'error' event is emitted and no listener is registered for it, Node.js throws an exception and may crash the program. Always register an error listener when working with EventEmitter in production code:
const EventEmitter = require('events');
const myEmitter = new EventEmitter();
myEmitter.on('error', function(err) {
console.log("Error caught:", err.message);
});
myEmitter.emit('error', new Error("Something went wrong!"));
Output:
Error caught: Something went wrong!
Key Points
- Node.js uses an event-driven model: events are emitted and listeners respond.
- The
eventsmodule provides theEventEmitterclass — the backbone of Node.js's event system. .on(event, listener)registers a listener;.emit(event)fires it.- Arguments can be passed to listeners through
.emit(). - Multiple listeners can be registered for the same event — all run in order when emitted.
.once()registers a listener that fires only the first time the event is emitted..off()or.removeListener()removes a specific listener from an event.- Custom classes can extend
EventEmitterto build event-driven systems. - The
'error'event must always have a listener to prevent the application from crashing.
