Documentation Index
Fetch the complete documentation index at: https://resources.devweekends.com/llms.txt
Use this file to discover all available pages before exploring further.
Events & EventEmitter
Node.js is built around an event-driven architecture. This means that certain objects (called “emitters”) emit named events that cause Function objects (“listeners”) to be called. The Fire Alarm Analogy: Think of EventEmitter like a building’s fire alarm system. The smoke detector (emitter) does not know which people (listeners) are in the building or what they will do when the alarm sounds. Some will grab their laptops, some will call 911, some will head for the exits. The detector’s only job is to broadcast the event. This decoupling—the emitter does not need to know about the listeners—is what makes event-driven architecture so powerful and flexible. For example, anet.Server object emits an event each time a peer connects to it; an fs.ReadStream emits an event when the file is opened; a stream emits an event whenever data is available to be read.
All objects that emit events are instances of the EventEmitter class.
The events Module
To use events, we need the events module.
Basic Usage
Registering a Listener
Use.on() to register a listener function for a specific event.
Emitting an Event
Use.emit() to trigger the event.
Passing Arguments
You can pass arguments to the event listener.Extending EventEmitter
In real-world applications, you usually extend theEventEmitter class to create your own modules that emit events.
Let’s create a Logger class that emits a ‘message’ event whenever a message is logged.
logger.js
Handling Errors
When an error occurs within anEventEmitter instance, the typical action is for an ‘error’ event to be emitted. If an EventEmitter does not have at least one listener registered for the ‘error’ event, and an ‘error’ event is emitted, the error is thrown, a stack trace is printed, and the Node.js process exits.
This is one of the most important patterns in Node.js: an unhandled ‘error’ event will crash your entire process. In production, this means your server goes down for all users because of a single unhandled error in one EventEmitter instance. Always register an ‘error’ listener.
Advanced Event Patterns
One-Time Listeners
Use.once() for listeners that should only fire once.
Removing Listeners
Listener Count and Names
Prepending Listeners
Listeners are normally added to the end of the queue. Use.prependListener() to add to the beginning.
Building a Custom Event-Driven System
Let’s build a practical example: a task queue system.Async Iterator Support
Node.js 12+ supports async iterators with events:EventEmitter vs Other Patterns
When should you use EventEmitter versus callbacks, promises, or streams? This decision comes up often in Node.js design:| Pattern | Communication | Coupling | Best for |
|---|---|---|---|
| Callback | One caller, one handler | Tight — caller must know the handler | Single async operation with one result |
| Promise / async-await | One caller, one result | Moderate — chain of .then() or await | Sequential async operations |
| EventEmitter | One emitter, many listeners | Loose — emitter does not know listeners | Multiple consumers, ongoing events, plugin systems |
| Stream | One producer, one consumer (piped) | Moderate — connected via pipe | Ordered data flow with backpressure |
- If a thing happens once and you need the result: use a Promise.
- If a thing happens multiple times and you need to notify one consumer: use a Stream.
- If a thing happens multiple times and you need to notify many consumers who do different things: use EventEmitter.
- If you are building a plugin system where external code should react to internal events without modifying the core: EventEmitter is the natural fit.
.on() are synchronous — they run immediately and in order when .emit() is called. But if a listener throws, it throws synchronously in the .emit() call, which can break your Promise chain in unexpected ways. Wrap .emit() in a try/catch if your listeners are not fully under your control.
Best Practices
Summary
- EventEmitter is the foundation of Node.js async patterns—streams, HTTP servers, and most core APIs are built on it
- Use
.on()to register listeners,.emit()to trigger events - Always handle ‘error’ events to prevent crashes—this is non-negotiable in production
- Use
.once()for one-time events like connection establishment or initialization - Extend EventEmitter to create your own event-driven classes
- Clean up listeners with
.off()or.removeListener()to prevent memory leaks in long-running processes - Watch for
MaxListenersExceededWarningas an early signal of memory leaks
EventEmitter Performance Characteristics
| Operation | Performance | Notes |
|---|---|---|
emit() with 0 listeners | ~50ns | Nearly free — no work to do |
emit() with 1 listener | ~100ns | Direct function call |
emit() with 10 listeners | ~500ns | Linear in listener count |
emit() with 100 listeners | ~5us | Still fast, but consider if this is a design smell |
on() / addListener() | O(1) | Pushes to internal array |
removeListener() | O(n) | Searches array by reference; use named functions to make this practical |
prependListener(), that listener jumps to the front. Mixing on() and prependListener() on the same event across multiple modules can create execution orders that are difficult to reason about. If ordering matters, document it explicitly or use a single orchestrating listener.