14. Event Emitter
Problem statement
In modern application development, the asynchronous event-driven architecture plays a crucial role, especially in handling operations where the response time is uncertain. This architecture allows for a decoupled system where objects (emitters) can emit events at any time, and function objects (listeners) respond to these events as they are triggered.
The challenge here is to implement an EventEmitter class that encapsulates this behavior. The class should provide mechanisms to:
- Emit events: Trigger an event by name, executing all callbacks subscribed to this event.
- Subscribe to events: Allow functions to listen for specific events and respond when those events are emitted. Each subscription should provide a way to release (unsubscribe) itself to avoid memory leaks and unintended behavior.
The EventEmitter class should work as follows:
class EventEmitter {
constructor() {
this.events = {};
}
subscribe(eventName, callback) {
// Logic to add a listener for the event
}
emit(eventName, ...args) {
// Logic to trigger an event and call all subscribed listeners
}
}
With this setup, you can create an instance of EventEmitter, subscribe functions to listen for named events, and emit those events at any point in your application. This approach is fundamental in creating non-blocking, asynchronous flows and is at the heart of many JavaScript frameworks and libraries.
This task will test your ability to work with higher-order functions, callbacks, and the principles of event-driven programming. Implementing the EventEmitter class will deepen your understanding of these concepts, which are pivotal in modern JavaScript development.
Requirements & constraints
- The EventEmitter class must be able to handle an unlimited number of event names and listeners for each event. - Listeners should be called in the order they were added for a specific event.
- Listeners should be called in the order they were added for a specific event.
- Each subscription should return an object with a release method that, when called, removes the listener from the event. This prevents memory leaks and ensures that listeners are not called after being unsubscribed.
- Emitting an event should call all listeners subscribed to that event with the provided arguments.
- The EventEmitter should not use any external libraries or dependencies; it must be implemented with vanilla JavaScript.
- Consider performance implications when adding, removing, and emitting events, especially for events with a large number of listeners.
How to approach 14. Event Emitter
The strategy an interviewer expects you to reach for.
A classic EventEmitter is pub/sub storage: map event names to listener lists and fan out on emit. Naive move One global callback, or mutate the listener array while iterating emit. Where it breaks unsubscribe mid-emit can skip listeners. Duplicate subscribes may double-fire. Missing events should no-op cleanly. The fix Store arrays or Sets per event. subscribe pushes and returns unsubscribe. emit iterates a snapshot. Prefer Set if exact duplicate references should collapse. Remember: Snapshot on emit; unsubscribe must remove the exact callback reference. Say this in the interview: I'd keep a list of listeners per event, return an unsubscribe function from subscribe, and emit over a copied list so removals during emit stay safe.
The full solution is part of HelloFrontend Pro
The question above is free to read in full. Upgrade to unlock the interactive workspace and the senior-level walkthrough that go with it.
- Runnable editor with the hidden test suite
- Progressive hints that unlock as you get stuck
- Senior-level reference solution with a line-by-line walkthrough
Already a member? Log in to open the workspace.