The Observer pattern defines a one-to-many dependency: when one object changes state, all dependents are notified. JavaScript's EventEmitter is a built-in implementation of this pattern.
// Observer pattern from scratch
interface Observer<T> {
update(data: T): void;
}
class EventEmitter<T> {
private observers: Observer<T>[] = [];
subscribe(observer: Observer<T>) {
this.observers.push(observer);
return () => this.unsubscribe(observer); // returns unsubscribe fn
}
unsubscribe(observer: Observer<T>) {
this.observers = this.observers.filter(o => o !== observer);
}
notify(data: T) {
this.observers.forEach(o => o.update(data));
}
}
// Usage
const userCreated = new EventEmitter<{ id: number; email: string }>();
userCreated.subscribe({ update: (u) => sendWelcomeEmail(u.email) });
userCreated.subscribe({ update: (u) => addToAnalytics(u.id) });
userCreated.notify({ id: 1, email: '[email protected]' });// Node.js EventEmitter
import { EventEmitter } from 'node:events';
class OrderService extends EventEmitter {
async createOrder(data: OrderData) {
const order = await db.orders.create(data);
this.emit('order:created', order);
return order;
}
}
const orders = new OrderService();
orders.on('order:created', (order) => {
console.log(\`New order: \${order.id}\`);
emailService.sendConfirmation(order.userId);
});
orders.once('order:created', () => {
analyticsService.trackFirstOrder();
});