The problem
Need event-driven communication between components
The solution
class EventEmitter {
constructor() {
this.events = {};
}
on(event, listener) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(listener);
return this;
}
off(event, listener) {
if (!this.events[event]) return this;
if (listener) {
this.events[event] = this.events[event].filter(l => l !== listener);
} else {
delete this.events[event];
}
return this;
}
emit(event, ...args) {
if (!this.events[event]) return false;
const listeners = this.events[event].slice();
listeners.forEach(listener => {
try {
listener.apply(this, args);
} catch (error) {
console.error(`Error in event listener for "${event}":`, error);
}
});
return true;
}
once(event, listener) {
const onceListener = (...args) => {
this.off(event, onceListener);
listener.apply(this, args);
};
return this.on(event, onceListener);
}
listeners(event) {
return this.events[event] ? this.events[event].slice() : [];
}
removeAllListeners() {
this.events = {};
return this;
}
}Put it to work
const emitter = new EventEmitter();
// Subscribe to events
emitter.on('userLogin', (user) => {
console.log(`User logged in: ${user.name}`);
});
emitter.on('userLogin', (user) => {
console.log(`Sending welcome email to: ${user.email}`);
});
// One-time listener
emitter.once('firstLogin', (user) => {
console.log(`First login for: ${user.name}`);
});
// Emit events
emitter.emit('userLogin', { name: 'John', email: '[email protected]' });
// "User logged in: John"
// "Sending welcome email to: [email protected]"
emitter.emit('firstLogin', { name: 'Jane' });
// "First login for: Jane"
emitter.emit('firstLogin', { name: 'Bob' });
// (Nothing happens - one-time listener already removed)
// Remove specific listener
const listener = (data) => console.log('Listener:', data);
emitter.on('test', listener);
emitter.off('test', listener);