Events and EventEmitter
An event is a message saying "something happened" that other parts of the program can react to. In the browser you subscribed to clicks with addEventListener. In Node.js the same role is played by the EventEmitter class from the node:events module. The server, streams and many other built-in objects are built on it.
Subscribing and emitting
on() subscribes a function to an event, emit() sends the event and passes data to every subscriber:
import { EventEmitter } from 'node:events';
const blog = new EventEmitter();
blog.on('post', (title) => {
console.log(`New post: ${title}`);
});
blog.emit('post', 'Hello, Node.js');
blog.emit('post', 'Working with files');Two "New post: ..." lines are printed. The event name is any string you choose.
Several subscribers
Any number of functions can subscribe to one event — they are called in the order they subscribed. This way different parts of the program react to the same thing without knowing about each other:
import { EventEmitter } from 'node:events';
const blog = new EventEmitter();
blog.on('comment', (comment) => console.log(`Writing to the log: ${comment.author}`));
blog.on('comment', (comment) => console.log(`Email to the author of post ${comment.postId}`));
blog.emit('comment', { postId: 1, author: 'Oleg', text: 'Thanks!' });Your own class with events
Usually you extend EventEmitter with your own class. Create the file src/blog-events.js:
// src/blog-events.js
import { EventEmitter } from 'node:events';
export class Blog extends EventEmitter {
#posts = [];
addPost(title) {
const post = { id: this.#posts.length + 1, title };
this.#posts.push(post);
this.emit('post:created', post);
return post;
}
}Use it in app.js:
// app.js
import { Blog } from './src/blog-events.js';
const blog = new Blog();
blog.on('post:created', (post) => console.log(`Published: #${post.id} ${post.title}`));
blog.addPost('Hello, Node.js');node app.js prints "Published: #1 Hello, Node.js". The Blog class does not know who listens — it only announces the event.
One-time handlers and unsubscribing
| Method | What it does |
|---|---|
on(name, fn) | Subscribes a function |
once(name, fn) | Subscribes for a single call |
off(name, fn) | Unsubscribes a function |
emit(name, ...data) | Sends an event |
listenerCount(name) | Number of subscribers |
import { EventEmitter } from 'node:events';
const server = new EventEmitter();
server.once('ready', () => console.log('Server is ready'));
server.emit('ready');
server.emit('ready');
console.log(server.listenerCount('ready')); // 0"Server is ready" is printed once: after the first call the handler removes itself.
Common mistake
The error event is special. If it is emitted and nobody listens, EventEmitter throws the error and the program crashes. So for objects that can report errors, always subscribe to error:
import { EventEmitter } from 'node:events';
const blog = new EventEmitter();
blog.on('error', (error) => console.log(`Error: ${error.message}`));
blog.emit('error', new Error('could not save the post'));Without the blog.on('error', ...) line this code would end with an unhandled error.