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.

Frequently asked questions

How does JavaScript handle event bus?
This task is covered in 2 stacks on this page: JavaScript, TypeScript. The "Observer / Event Emitter Pattern" snippet in JavaScript uses `class EventEmitter { on(event, handler) {} emit(event, data) {} }`.
Which code does the JavaScript example use?
The "Observer / Event Emitter Pattern" snippet uses `class EventEmitter { on(event, handler) {} emit(event, data) {} }`, from the Common Patterns section of the JavaScript cheat sheet.
Which stacks cover "event bus" on this page?
JavaScript, TypeScript. Together they hold 2 copy-ready snippets for this task.
Is there anything to watch out for?
Yes. For "Observer / Event Emitter Pattern": The observer pattern decouples event producers from consumers. Returning an unsubscribe function from on() prevents memory leaks.