EventEmitter is the built-in class that powers Node's events pattern. This lesson shows how to use it directly and how to build your own custom event-driven classes on top of it.
The EventEmitter Class
EventEmitter, from the node:events module, is the class every built-in emitter (HTTP servers, streams, etc.) is either an instance of or extends. You can use it directly, or extend it in your own classes to give them the same .on()/.emit() API.
Internally, an EventEmitter just keeps a map of event names to arrays of listener functions, .on() pushes onto that array, and .emit() calls every function in it, passing along whatever arguments you gave to .emit().
You can also use EventEmitter directly, without extending it, whenever you just need a standalone pub/sub-style object.
Custom EventEmitter Classes
class OrderProcessor extends EventEmitter {
process(order) {
// ... do work ...
this.emit('processed', order);
}
}
Extend EventEmitter to give your own class .on()/.emit()/.once() methods automatically.
Call this.emit('eventName', ...) from inside your class's methods when something noteworthy happens.
Consumers of your class can then .on('eventName', handler) without knowing your implementation details.
This decouples the class doing the work from the code reacting to its results.
EventEmitter Cheatsheet
The methods you'll use most on any EventEmitter instance.
Method
Purpose
.on(event, fn)
Add a listener that runs every time
.once(event, fn)
Add a listener that runs only the first time
.off(event, fn)
Remove a specific listener
.emit(event, ...args)
Fire an event, calling all its listeners
.listenerCount(event)
Count listeners registered for an event
.setMaxListeners(n)
Raise the default 10-listener warning threshold
Building a Custom Event-Driven Class
Extending EventEmitter is a common way to build objects that represent ongoing work, an order processor, a background job runner, a WebSocket wrapper, letting other parts of your code react to lifecycle moments without polling or tight coupling.
By default, EventEmitter warns in the console if more than 10 listeners are attached to a single event on the same emitter, this is a safeguard against accidental memory leaks (like re-registering a listener inside a loop). You can raise this limit deliberately with setMaxListeners() when it's genuinely expected.
The default limit is a warning, not a hard error, code keeps running past it.
Seeing this warning unexpectedly usually means a listener is being added repeatedly instead of once.
Call emitter.setMaxListeners(20) (or 0 for unlimited) only when you're sure the higher count is intentional.
Common Mistakes
Registering a new listener inside a loop or a frequently-called function, causing listener count to grow unbounded.
Extending EventEmitter but forgetting to call events consistently, making the class's behavior hard to predict.
Ignoring the MaxListeners warning instead of investigating why so many listeners accumulated.
Using .emit() expecting it to behave asynchronously, listeners run synchronously in the same tick.
Key Takeaways
EventEmitter (from node:events) is the class underlying Node's entire events pattern.
Extend EventEmitter to give custom classes their own .on()/.emit() API.
.emit() calls listeners synchronously, in the order they were registered.
The default 10-listener warning helps catch accidental listener leaks early.
Pro Tip
If you extend EventEmitter for a class that will be long-lived (like a connection pool), also provide a way to remove listeners (.off()/.removeAllListeners()) when consumers are done, otherwise listeners can silently accumulate and leak memory over the app's lifetime.
You can now build your own event-driven classes. Next, learn Node.js Streams, one of the most powerful (and most EventEmitter-based) core APIs.