What is EventEmitter?
The `EventEmitter` is a core module in Node.js that facilitates an event-driven architecture, enabling objects to emit named events that cause registered listener functions to be called. It's a fundamental part of many Node.js APIs and is widely used for building custom event systems.
What is EventEmitter?
At its heart, EventEmitter implements the observer pattern (also known as the publish/subscribe pattern). An object that extends or uses EventEmitter can emit specific events. Other parts of the application can then 'listen' for these events and execute a callback function whenever the event is emitted.
Many built-in Node.js modules, such as fs.ReadStream, http.Server, and net.Socket, inherit from EventEmitter to signal changes in their state or operations.
Key Methods
`on(eventName, listener)` or `addListener(eventName, listener)`
Registers a function to be called whenever a specified event is emitted. The listener function will be invoked every time the event occurs.
const EventEmitter = require('events');
const myEmitter = new EventEmitter();
myEmitter.on('userLoggedIn', (username) => {
console.log(`${username} has logged in.`);
});
`emit(eventName, [...args])`
Synchronously calls each of the listeners registered for the event named eventName, in the order they were registered. Any arguments passed to emit after the eventName are passed to the listener functions.
myEmitter.emit('userLoggedIn', 'Alice'); // Output: Alice has logged in.
myEmitter.emit('userLoggedIn', 'Bob'); // Output: Bob has logged in.
`once(eventName, listener)`
Adds a one-time listener function for the event named eventName. The next time eventName is emitted, this listener is invoked and then removed.
myEmitter.once('firstLogin', (username) => {
console.log(`${username} is logging in for the first time!`);
});
myEmitter.emit('firstLogin', 'Charlie'); // Output: Charlie is logging in for the first time!
myEmitter.emit('firstLogin', 'David'); // No output for David, as the listener was removed.
`removeListener(eventName, listener)` or `off(eventName, listener)`
Removes a specific listener from the listener array for the event named eventName. Note that this requires a reference to the exact function that was registered.
const logActivity = (msg) => console.log(`Activity: ${msg}`);
myEmitter.on('activity', logActivity);
myEmitter.emit('activity', 'User did something'); // Output: Activity: User did something
myEmitter.removeListener('activity', logActivity);
myEmitter.emit('activity', 'User did something else'); // No output
Why Use It?
- Decoupling Components: Allows different parts of your application to communicate without having direct knowledge of each other. A component can emit an event, and other components can react to it without the emitter knowing who or what is listening.
- Asynchronous Operations: Often used to signal the completion or progress of asynchronous tasks.
- Custom Events: You can define your own custom events for specific application logic, making your code more modular and reactive.
- Building Event-Driven APIs: Essential for creating APIs and libraries where actions are triggered by events, similar to how DOM events work in browsers.
Simple Example
const EventEmitter = require('events');
class MyCoolService extends EventEmitter {
constructor() {
super();
this.data = [];
}
addData(item) {
this.data.push(item);
this.emit('dataAdded', item, this.data.length);
if (this.data.length >= 3) {
this.emit('dataLimitReached', this.data.length);
}
}
removeData(item) {
const index = this.data.indexOf(item);
if (index > -1) {
this.data.splice(index, 1);
this.emit('dataRemoved', item, this.data.length);
}
}
}
const service = new MyCoolService();
service.on('dataAdded', (item, count) => {
console.log(`[Listener 1] New item added: ${item}. Total items: ${count}`);
});
service.on('dataAdded', (item) => {
console.log(`[Listener 2] A new item '${item}' was just added.`);
});
service.once('dataLimitReached', (limit) => {
console.log(`[One-time Listener] The data limit of ${limit} has been reached!`);
});
service.addData('apple');
service.addData('banana');
service.addData('cherry'); // This will trigger dataLimitReached
service.addData('date'); // dataLimitReached will not trigger again
service.removeData('banana');