The EventEmitter Class
EventEmitter is the concrete implementation of Node's event-driven model. It is a small, elegant class: an internal map from event names to arrays of listener functions, plus methods to add, remove, and fire them. Because so much of Node inherits from it — servers, streams, sockets, process — understanding the class itself pays off everywhere.
Creating one
import { EventEmitter } from 'node:events'
const emitter = new EventEmitter()
emitter.on('greet', (name) => console.log(`Hello, ${name}`))
emitter.emit('greet', 'Ada') // → Hello, AdaHello, Ada
What lives inside
Conceptually, an emitter is just a registry. Picturing the internal structure demystifies every method:
The emitter's internal state
emitter._events = {
'greet': [ fn1 ], // one listener
'data': [ fnA, fnB, fnC ], // three, called in this order
'error': [ errHandler ],
}
emit('data', x) → fnA(x); fnB(x); fnC(x) // synchronous, in array orderThe full method set
Method | Effect |
|---|---|
| Append a listener (alias |
| Listener that removes itself after one call |
| Add to the front of the queue |
| Call every listener for |
| Remove a specific listener (alias |
| Clear listeners for one or all events |
| Array of the current listener functions |
| Number of listeners attached |
| Raise/lower the leak-warning threshold |
emit returns whether anyone listened
emit returns true if the event had listeners, false otherwise — useful for "fire only if someone cares" logic:
const e = new EventEmitter()
console.log(e.emit('nobody')) // false — no listeners
e.on('hi', () => {})
console.log(e.emit('hi')) // true — one listener ranthis inside a listener
emitter.on('ping', function () {
console.log(this === emitter) // true — regular function
})
emitter.on('ping', () => {
console.log(this === emitter) // false — arrow keeps outer this
})Order, and jumping the queue
Listeners fire in the order added. prependListener and prependOnceListener insert at the front when you need a handler to run first:
const e = new EventEmitter()
e.on('x', () => console.log('second'))
e.prependListener('x', () => console.log('first'))
e.emit('x')first second
Inherit it for your own types
The standard pattern is class X extends EventEmitter. Always call super() so the internal registry is initialized:
import { EventEmitter } from 'node:events'
class Timer extends EventEmitter {
start(ms) {
this.emit('start')
setTimeout(() => this.emit('done'), ms)
return this // chainable, like Node's own emitters
}
}
new Timer()
.on('start', () => console.log('go'))
.on('done', () => console.log('finished'))
.start(100)