Glossary / Event emitters
Event emitters
Node.js EventEmitter is a synchronous in-process publish/subscribe mechanism: emitters invoke registered listeners for a named event, passing supplied arguments. It is useful for lifecycle notifications and decoupling components, but it is not a message queue, does not inherently provide backpressure, and listeners run on the emitting call stack.
Registration and invocation semantics
on() registers a listener each time it is called; registering the same function repeatedly creates repeated calls. once() removes its listener before or during its first invocation. removeListener()/off() removes a matching registration. Listeners are invoked synchronously in registration order for that emission, so a slow listener blocks the emitter and exceptions can escape from emit().
import { EventEmitter } from 'node:events';
const bus = new EventEmitter();
bus.once('ready', value => console.log(value));
bus.emit('ready', 42); // prints 42 synchronously
The special error event has process-level significance: emitting it without a registered listener throws the error and can terminate the process. EventEmitter does not automatically await promises returned by async listeners. Rejections may become unhandled; use explicit try/catch, a wrapper, or carefully consider captureRejections and its behavior.
Design and failure modes
Events reduce direct coupling but can hide control flow, ownership, and ordering. A listener added after an event has fired does not receive history. There is no built-in replay, durability, schema validation, listener isolation, or queue bound. maxListeners is a leak warning threshold, not a hard cap. Remove listeners when their owner ends, especially for long-lived emitters.
Do not emit an event and assume every observer succeeded. If all handlers must complete or their errors must affect the operation, use explicit function calls or a defined async orchestration API. Event names and payloads should be stable and documented; avoid exposing mutable payload objects that listeners can unexpectedly change.
Interview questions
- Are listeners invoked asynchronously? No, standard EventEmitter listeners run synchronously during
emit(). - What is special about
error? Without an error listener, emission throws and may terminate the process. - Does emit await async listeners? No; returned promises are not awaited automatically.
- Why can duplicate registration be a bug? Each registration results in another invocation and may retain closures.
- When prefer direct calls? When completion, return values, or error propagation across participants must be explicit.