Design an Event Emitter
The small event bus drill: subscribe handlers, emit in order, unsubscribe by function identity, then add once with a snapshot-safe loop.
- Pattern
- Map event -> ordered handlers
- Time
- O(listeners)
- Space
- O(listeners)
An event emitter is one of the cleanest reps for this, method APIs, callback identity, and mutation while iterating.
The recipe
Say before you type: "Map event names to ordered handlers. emit copies the list, then calls each handler."
Start with:
private handlers = new Map<string, Set<Handler>>();
A Set keeps insertion order and prevents duplicate handler references.
off
off(event, handler) depends on function identity. This works:
const handler = () => {};
emitter.on("save", handler);
emitter.off("save", handler);
This does not:
emitter.off("save", () => {});
That second arrow is a different function.
once
once() wraps the original handler:
const wrapper = (payload) => {
off(event, wrapper);
handler(payload);
};
Register the wrapper through on, so once and on handlers share one list and stay in subscription order — a once registered first still runs first. A separate once-list is the tempting shortcut and it silently reorders every emit.
Make emit() iterate over [...handlers] so removing during emit does not corrupt the loop.