Event bus

2 snippets across 2 stacks — JavaScript, TypeScript

JSJavaScript

Observer / Event Emitter Pattern

JS · Common Patterns
syntax
class EventEmitter { on(event, handler) {} emit(event, data) {} }
example
class EventBus {
  #handlers = new Map();

  on(event, handler) {
    if (!this.#handlers.has(event)) {
      this.#handlers.set(event, new Set());
    }
    this.#handlers.get(event).add(handler);
    return () => this.off(event, handler); // unsubscribe fn
  }

  off(event, handler) {
    this.#handlers.get(event)?.delete(handler);
  }

  emit(event, data) {
    this.#handlers.get(event)?.forEach(fn => fn(data));
  }
}

const bus = new EventBus();
const unsub = bus.on("user:login", (user) => console.log(`Welcome, ${user}`));
bus.emit("user:login", "Ava"); // "Welcome, Ava"
unsub(); // unsubscribes

Note The observer pattern decouples event producers from consumers. Returning an unsubscribe function from on() prevents memory leaks.

TSTypeScript

Type-Safe Event Emitter

TS · Common Patterns
syntax
interface Events {
  eventName: (payload: Type) => void;
}
class Emitter<T extends Record<string, (...args: any[]) => void>> { ... }
example
type EventMap = {
  userLogin: (userId: string, timestamp: Date) => void;
  purchase: (orderId: string, amount: number) => void;
  error: (error: Error) => void;
};

class TypedEmitter<T extends Record<string, (...args: any[]) => void>> {
  private handlers = new Map<keyof T, Set<Function>>();

  on<K extends keyof T>(event: K, handler: T[K]): void {
    if (!this.handlers.has(event)) this.handlers.set(event, new Set());
    this.handlers.get(event)!.add(handler);
  }

  emit<K extends keyof T>(event: K, ...args: Parameters<T[K]>): void {
    this.handlers.get(event)?.forEach(fn => fn(...args));
  }
}

const bus = new TypedEmitter<EventMap>();
bus.on("purchase", (orderId, amount) => {
  console.log(`Order ${orderId}: $${amount}`);
});
bus.emit("purchase", "ord_1", 49.99);
output
// Full autocomplete on event names, parameter types, and callback signatures

Note This pattern maps event names to callback signatures. Parameters<T[K]> extracts the argument tuple so emit() is fully typed. This is the foundation used by libraries like mitt, EventEmitter3, and socket.io.