EventsAPI
Node API: Events
Describes the surface of the events
built-in module provided as part of the Node API; the events
API provides types like EventEmitter, EventTarget, Event, and EventListener to work with events in Node.js.
Events in Node behave similarly to DOM (browser) events, with a few caveats.
For information about how the Node API behaves and how it differs from browsers, see the Node.js Events documentation.
Node Events
Much of the Node.js core API is built around an idiomatic asynchronous event-driven architecture in which certain kinds of objects (called "emitters") emit named events that cause Function objects ("listeners") to be called.
For instance: a net.Server
object emits an event each time a peer connects to it; a 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. These objects expose an EventEmitter.on function that allows one or more functions to be attached to named events emitted by the object. Typically, event names are camel-cased strings but any valid JavaScript property key can be used.
When the EventEmitter object emits an event, all functions attached to that specific event are called synchronously. Any values returned by the called listeners are ignored and discarded.
The following example shows a simple EventEmitter instance with a single listener. The EventEmitter.on method is used to register listeners, while the EventEmitter.emit method is used to trigger the event:
import { EventEmitter } from 'node:events';
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
console.log('an event occurred!');
});
myEmitter.emit('event');
Passing arguments and this
to listeners
The EventEmitter.emit method allows an arbitrary set of arguments to be passed to the listener functions. Keep in mind that when an ordinary listener function is called, the standard this
keyword is intentionally set to reference the EventEmitter instance to which the listener is attached.
import { EventEmitter } from 'node:events';
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on('event', function(a, b) {
console.log(a, b, this, this === myEmitter);
// Prints:
// a b MyEmitter {
// _events: [Object: null prototype] { event: [Function (anonymous)] },
// _eventsCount: 1,
// _maxListeners: undefined,
// [Symbol(shapeMode)]: false,
// [Symbol(kCapture)]: false
// } true
});
myEmitter.emit('event', 'a', 'b');
It is possible to use ES6 Arrow Functions as listeners, however, when doing so, the this
keyword will no longer reference the EventEmitter instance:
import { EventEmitter } from 'node:events';
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on('event', (a, b) => {
console.log(a, b, this);
// Prints: a b {}
});
myEmitter.emit('event', 'a', 'b');
Asynchronous vs. synchronous
The EventEmitter calls all listeners synchronously in the order in which they were registered. This ensures the proper sequencing of events and helps avoid race conditions and logic errors. When appropriate, listener functions can switch to an asynchronous mode of operation using the setImmediate()
or process.nextTick()
methods:
import { EventEmitter } from 'node:events';
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on('event', (a, b) => {
setImmediate(() => {
console.log('this happens asynchronously');
});
});
myEmitter.emit('event', 'a', 'b');
Handling events only once
When a listener is registered using the EventEmitter.on method, that listener is invoked every time the named event is emitted.
import { EventEmitter } from 'node:events';
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
let m = 0;
myEmitter.on('event', () => {
console.log(++m);
});
myEmitter.emit('event');
// Prints: 1
myEmitter.emit('event');
// Prints: 2
Using the EventEmitter.once method, it is possible to register a listener which is called at most once for a particular event. Once the event is emitted, the listener is unregistered and then called.
import { EventEmitter } from 'node:events';
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
let m = 0;
myEmitter.once('event', () => {
console.log(++m);
});
myEmitter.emit('event');
// Prints: 1
myEmitter.emit('event');
// Ignored
Error events
When an error occurs within an EventEmitter instance, the typical action is for an 'error'
event to be emitted. These are treated as special cases within Node.js.
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.
import { EventEmitter } from 'node:events';
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.emit('error', new Error('whoops!'));
// Throws and crashes Node.js
To guard against crashing the Node.js process the domain
module can be used. (Note, however, that the node:domain
module is deprecated.)
As a best practice, listeners should always be added for the 'error'
events.
import { EventEmitter } from 'node:events';
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on('error', (err) => {
console.error('whoops! there was an error');
});
myEmitter.emit('error', new Error('whoops!'));
// Prints: whoops! there was an error
It is possible to monitor 'error'
events without consuming the emitted error by installing a listener using the symbol events.errorMonitor
.
import { EventEmitter, errorMonitor } from 'node:events';
const myEmitter = new EventEmitter();
myEmitter.on(errorMonitor, (err) => {
MyMonitoringTool.log(err);
});
myEmitter.emit('error', new Error('whoops!'));
// Still throws and crashes Node.js
Capture rejections of promises
Using async
functions with event handlers is problematic, because it can lead to an unhandled rejection in case of a thrown exception:
import { EventEmitter } from 'node:events';
const ee = new EventEmitter();
ee.on('something', async (value) => {
throw new Error('kaboom');
});
The captureRejections
option in the EventEmitter constructor or the global setting change this behavior, installing a .then(undefined, handler)
handler on the Promise. This handler routes the exception asynchronously to the Symbol.for('nodejs.rejection')
method if there is one, or to 'error'
event handler if there is none.
import { EventEmitter } from 'node:events';
const ee1 = new EventEmitter({ captureRejections: true });
ee1.on('something', async (value) => {
throw new Error('kaboom');
});
ee1.on('error', console.log);
const ee2 = new EventEmitter({ captureRejections: true });
ee2.on('something', async (value) => {
throw new Error('kaboom');
});
ee2[Symbol.for('nodejs.rejection')] = console.log;
Setting events.captureRejections = true
will change the default for all new instances of EventEmitter.
import { EventEmitter } from 'node:events';
EventEmitter.captureRejections = true;
const ee1 = new EventEmitter();
ee1.on('something', async (value) => {
throw new Error('kaboom');
});
ee1.on('error', console.log);
The 'error'
events that are generated by the captureRejections
behavior do not have a catch handler to avoid infinite error loops: the recommendation is to not use async functions as 'error'
event handlers.
See also
for the base class for all events
for the extension point used to create custom event types
for the interface supported for targets, or subjects, of events
for the interface supported for objects that emit events
Properties
If set to true
, the 'rejectionHandled'
event is emitted whenever a Promise is rejected, but there are no listeners to handle the rejection. This event is emitted with the following arguments:
By default, a maximum of 10 listeners can be registered for any single event. This limit can be changed for individual EventEmitter instances using the emitter.setMaxListeners(n)
method. To change the default for all EventEmitter instances, the events.defaultMaxListeners property can be used. If this value is not a positive number, a RangeError is thrown.
This symbol shall be used to install a listener for only monitoring 'error'
events. Listeners installed using this symbol are called before the regular 'error'
listeners are called.
Functions
Listens once to the abort
event on the provided signal
.
Returns an array listing the events for which the emitter has registered listeners. The values in the array are strings or Symbols.
Returns the current max listener value for the given emitter or target.
Returns the number of listeners listening to the event named eventName.
Returns the number of listeners listening to the event named event
.
Adds the listener function to the end of the listeners array for the event named name
to the emitter
.
Adds a one-time listener function for the event named name
to the emitter
. The next time name
is triggered, this listener is removed and then invoked.
Sets the maximum number of listeners for the given emitter or target.