The events Module
node:events provides the EventEmitter class — the beating heart of Node's architecture. HTTP servers, streams, sockets, the process object, and countless libraries are all emitters. Master this one class and a huge swath of Node's API suddenly follows the same predictable shape: .on() to listen, .emit() to fire.
The publish/subscribe model
An emitter lets one piece of code announce that something happened without knowing who (if anyone) is listening. Listeners subscribe by name; emitting calls them all, synchronously, in registration order:
import { EventEmitter } from 'node:events'
const bus = new EventEmitter()
bus.on('order', (id, total) => {
console.log(`Order ${id}: $${total}`)
})
bus.emit('order', 42, 19.99) // → Order 42: $19.99Order 42: $19.99
The core methods
Method | Does |
|---|---|
| Subscribe; fires every time |
| Subscribe; fires once, then auto-removes |
| Fire the event, passing args to listeners |
| Unsubscribe a specific listener |
| Drop all listeners (for one event or all) |
| How many listeners are attached |
| Array of events with active listeners |
Emitting is synchronous
bus.on('x', () => console.log('listener'))
console.log('before')
bus.emit('x')
console.log('after')
// Output order: before → listener → after (NOT before → after → listener)The special 'error' event
const e = new EventEmitter()
// Without this listener, the next line would CRASH the process:
e.on('error', (err) => console.error('Handled:', err.message))
e.emit('error', new Error('boom')) // → Handled: boomThe max-listeners warning
To catch leaks, an emitter warns when more than 10 listeners are added for one event — usually a sign you're subscribing in a loop without cleaning up:
(node:123) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 order listeners added to [EventEmitter]. Use emitter.setMaxListeners() to increase limit
Extending EventEmitter
The idiomatic way to give your own class events is to extend EventEmitter — exactly how Node's own http.Server works:
import { EventEmitter } from 'node:events'
class Job extends EventEmitter {
async run() {
this.emit('start')
try {
const result = await this.doWork()
this.emit('done', result)
} catch (err) {
this.emit('error', err)
}
}
}
const job = new Job()
job.on('start', () => console.log('running…'))
job.on('done', (r) => console.log('finished', r))
job.on('error', (e) => console.error('failed', e))Modern helpers
import { once, on } from 'node:events'
// Await a single event as a promise:
const [result] = await once(job, 'done')
// Async-iterate a stream of events:
for await (const [data] of on(emitter, 'data')) {
console.log(data)
}