emitter.on()
Register a listener that runs every time the named event is emitted.
emitter.on(eventName, listener)- Added in Node.js v0.1.101
Parameters
| Parameter | Type | Description |
|---|---|---|
$eventName | <string> | <symbol> | The name of the event. |
$listener | <Function> | The callback function, invoked with whatever arguments were passed to emit(). |
Return value
<EventEmitter>A reference to the emitter, so calls can be chained.
Errors
Throws ERR_INVALID_ARG_TYPE if listener is not a function.
on() appends a listener to the end of the listener array for eventName. It is the default way to subscribe, and it is an alias of addListener() — they are the same function, so pick one and stay consistent.
When to reach for it
Use on() for anything that recurs: stream 'data', server 'request', a bus event fired once per record. If the event happens exactly once per object lifetime — 'ready', 'close', 'end' — reach for once() instead so the listener cleans itself up.
What trips people up
No deduplication. Node makes no check for whether the same function is already registered. Calling on() with the same pair twice means the handler runs twice per emit — a classic bug when subscription happens inside a request handler or a React-style effect that re-runs.
Listeners fire in registration order, and because on() returns the emitter, subscriptions chain. Inside a non-arrow listener, this is the emitter itself; an arrow function keeps the enclosing this instead, which is usually what you want anyway.
Example
When to use it
- Subscribe a logger and a metrics collector to the same 'request' event without either knowing about the other
- Accumulate stream chunks with socket.on('data', ...) until 'end' fires
- Wire a bounded set of handlers onto an app-wide message bus at startup
Frequently asked questions
What is the difference between on() and addListener() in Node.js?
There is none — on() is an alias for addListener() and both append to the same listener array. on() is the idiomatic spelling used throughout the Node docs and core modules.
Why is my event handler called twice?
Because on() was called twice with the same function; it does no duplicate checking. This usually happens when subscription code sits inside a function that runs more than once, such as a request handler or a reconnect routine. Check emitter.listenerCount(eventName) to confirm, then move the subscription to setup code or remove the old listener first.
Do event listeners run in the order they were added?
Yes. emit() walks the listener array front to back synchronously. Use prependListener() to put a handler at the front instead.
More examples
`this` inside a listener
A regular function listener is called with the emitter as `this`, which lets it call this.emit() or this.off() without a closure. An arrow function captures the surrounding `this` instead, so the second line prints false. Use arrows unless you specifically want the emitter binding.
const { EventEmitter } = require('node:events');
const bus = new EventEmitter();
bus.on('ping', function () {
console.log('function listener, this === bus:', this === bus);
});
bus.on('ping', () => {
console.log('arrow listener, this === bus:', this === bus);
});
bus.emit('ping');Subscribing inside a request handler leaks
The commented-out line is the most common EventEmitter memory leak: listeners accumulate for the lifetime of the process and Node eventually prints a MaxListenersExceededWarning after ten requests. Keeping a named reference and removing it on 'close' bounds the count.
const http = require('node:http');
const { EventEmitter } = require('node:events');
const bus = new EventEmitter();
const server = http.createServer((req, res) => {
// WRONG: a new listener is added on every single request.
// bus.on('tick', () => res.write('tick'));
// RIGHT: subscribe, then remove when the response is done.
const onTick = () => res.write('tick');
bus.on('tick', onTick);
res.on('close', () => bus.off('tick', onTick));
res.end('ok');
});
server.listen(3000);
Discussion