Coding problem · Medium · Design & Data Structures
Build an EventEmitter
Medium Language practicals Seen at Meta, Microsoft, Bloomberg
Implement a class EventEmitter with:
on(event, listener)— register a listener; returnthisfor chaining.off(event, listener)— remove that listener.once(event, listener)— listener runs on the next emit only.emit(event, ...args)— call listeners in registration order withargs; returntrueif there were listeners, otherwisefalse.
Listeners added or removed during an emit must not affect the listeners called for that emit.
Hints
Hint 1
Store Map<event, listener[]>.
Hint 2
In emit, iterate over a copy of the array.
Hint 3
once can wrap the listener in a function that removes itself first.
Write and run your solution
Loading the editor… (JavaScript is required to run code in your browser.)
Solution and explanation
Show the solution
Time O(k) per emit for k listeners · Space O(total listeners)
A map from event name to an array of listeners. emit iterates over a snapshot so listeners that unsubscribe themselves (like once) don't make the loop skip the next listener. once wraps the listener in a self-removing function and remembers the original so off(event, original) still works.
Reference solution
class EventEmitter {
constructor() {
this.listeners = new Map();
}
on(event, listener) {
const list = this.listeners.get(event) ?? [];
list.push(listener);
this.listeners.set(event, list);
return this;
}
off(event, listener) {
const list = this.listeners.get(event) ?? [];
this.listeners.set(event, list.filter((l) => l !== listener && l.original !== listener));
return this;
}
once(event, listener) {
const wrapper = (...args) => {
this.off(event, wrapper);
listener.apply(this, args);
};
wrapper.original = listener; // so off(event, listener) also removes a pending once()
return this.on(event, wrapper);
}
emit(event, ...args) {
const list = this.listeners.get(event);
if (!list || list.length === 0) return false;
for (const listener of [...list]) listener.apply(this, args);
return true;
}
}
Common mistakes
- Mutating the array while iterating it during
emit. oncelisteners that can't be removed withoff.- Using
setTimeout— Node's emitter is synchronous.
Follow-up questions
- Add
listenerCountand a max-listeners warning (memory-leak detection). - Support an
errorevent that throws when nobody listens, like Node.
More Design & Data Structures problems
- LRU Cache · Medium
- Token Bucket Rate Limiter · Medium
- Key-Value Store with TTL · Medium
- URL Shortener Codes (Base62) · Easy
- Row Lock Manager with Deadlock Detection · Hard
- Versioned Store with Optimistic Locking · Easy