Node.js Events
Node's EventEmitter class is the foundation of its event-driven architecture, letting objects emit named events that any number of listeners can react to.
The EventEmitter Class
Many of Node's core objects—HTTP servers, readable and writable streams, child processes—are built on top of the EventEmitter class from require('node:events'). Rather than returning a single value, these objects announce things as they happen by emitting named events, and any code that cares can subscribe to those events with a listener function. You can use EventEmitter directly by instantiating it, or extend it in your own classes to give them the same publish/subscribe behavior.
Registering and Emitting Events
The .on(eventName, listener) method registers a function to run every time that named event is emitted, and .emit(eventName, ...args) triggers the event, calling every registered listener synchronously, in the order they were added, and passing along any extra arguments. A single event can have any number of listeners, and none of them need to know about each other.
- emitter.on(event, listener) — registers a listener that runs every time the event fires
- emitter.once(event, listener) — registers a listener that runs at most one time, then removes itself
- emitter.emit(event, ...args) — synchronously calls all listeners registered for that event
- emitter.off(event, listener) (alias for removeListener) — unregisters a specific listener
- emitter.listenerCount(event) — reports how many listeners are currently registered for an event
Registering multiple listeners for one event
const { EventEmitter } = require('node:events');
const emitter = new EventEmitter();
emitter.on('userLoggedIn', (username) => {
console.log(`Welcome back, ${username}!`);
});
emitter.on('userLoggedIn', (username) => {
console.log(`Audit log: ${username} logged in at ${new Date().toISOString()}`);
});
emitter.emit('userLoggedIn', 'alice');
// Welcome back, alice!
// Audit log: alice logged in at 2026-...One-Time Listeners and Custom Emitters
Sometimes you only care about the first occurrence of an event—a 'ready' event fired once at startup, for example. once() covers that case: it behaves like on(), except the listener automatically detaches itself immediately after its first call, so calling emit() again for that event has no effect on it. When you no longer need a listener at all, off() (or its older alias removeListener()) removes it explicitly.
once() and removing listeners
const { EventEmitter } = require('node:events');
const emitter = new EventEmitter();
emitter.once('ready', () => console.log('Server initialized'));
emitter.emit('ready'); // Server initialized
emitter.emit('ready'); // (nothing happens — listener already removed)
function onWarning(msg) {
console.log('Warning:', msg);
}
emitter.on('warning', onWarning);
emitter.emit('warning', 'low disk space'); // Warning: low disk space
emitter.off('warning', onWarning);
emitter.emit('warning', 'low disk space'); // (nothing happens — listener removed)Extending EventEmitter in a custom class
const { EventEmitter } = require('node:events');
class Downloader extends EventEmitter {
start(fileName) {
this.emit('start', fileName);
// Simulate progress without a real network call
setTimeout(() => this.emit('progress', 50), 100);
setTimeout(() => this.emit('complete', fileName), 200);
}
}
const downloader = new Downloader();
downloader.on('start', (fileName) => console.log(`Starting download: ${fileName}`));
downloader.on('progress', (percent) => console.log(`Progress: ${percent}%`));
downloader.on('complete', (fileName) => console.log(`Finished: ${fileName}`));
downloader.start('report.pdf');Exercise: Node.js Events
In what order does an EventEmitter invoke multiple listeners registered for the same event?