Exams are finally over! That's why I decided to take up the challenge of the 30-Days-JavaScript LeetCode challenge. One problem that caught my attention was designing an EventEmitter class. In this post, we'll dive into the problem statement, explore my solution, and see how it can be used in practical scenarios! I personally believe that this would be an amazing coding interview question.
Highly recommend you to solve these questions: 30-days-of-javascript-leetcode
Understanding the problem
The task at hand was to implement an EventEmitter class with two essential methods: subscribe and emit. The subscribe method allows us to register callback functions for specific events, while the emit method triggers the registered callbacks when an event occurs.
Leetcode QuestionDesigning the EventEmitter Class
type Callback = (...args: any[]) => any;
type Subscription = {
unsubscribe: () => void;
};
class EventEmitter {
private events: Map<string, Callback[]> = new Map();
subscribe(eventName, callback) {
if (!this.events.has(eventName)) {
this.events.set(eventName, []);
}
const event = this.events.get(eventName);
event.push(callback);
return {
unsubscribe: () => {
const index = event.indexOf(callback);
if (index > -1) {
event.splice(index, 1);
}
},
};
}
emit(eventName, args = []) {
const event = this.events.get(eventName);
if (!event) {
return [];
}
return event.map(callback => callback(...args));
}
}
The EventEmitter class contains a Map called events to store the subscribed callbacks for each event name. The subscribe method checks if the event already exists in the map and creates an empty array if not. It then adds the new callback to the event's array of callbacks. The method returns an object with an unsubscribe method that removes the callback from the event's array of callbacks when called.
The emit method triggers the callbacks associated with a specific event. It retrieves the event's array of callbacks from the events map and invokes each callback with the provided arguments (or an empty array). The results of the callback invocations are returned as an array.
Practical Use Cases
Where could we use an EventEmitter?
-
User Interaction Events
const eventEmitter = new EventEmitter();
const clickSubscription = eventEmitter.subscribe('click', () => {
console.log('User clicked on the button');
});
// Trigger the 'click' event
eventEmitter.emit('click');
-
Data Loading Events
const eventEmitter = new EventEmitter();
const dataLoadedSubscription = eventEmitter.subscribe('dataLoaded', (data) => {
console.log('Data loaded:', data);
});
const data = fetchData(); // Simulate data loading
eventEmitter.emit('dataLoaded', data);
-
Custom Event Management
const eventEmitter = new EventEmitter();
const subscription = eventEmitter.subscribe('customEvent', (arg1, arg2) => {
console.log('Custom event emitted with arguments:', arg1, arg2);
});
// Emit the custom event with arguments
eventEmitter.emit('customEvent', 'Hello', 'World');
Conclusion
With the EventEmitter class in the power of our hands, we can handle user interactions, manage data loading events, and create custom event-driven systems. It opens up a world of possibilities!
I hope you found this post helpful in understanding event handling and building an EventEmitter. Now, go forth and add some event-driven magic to your JavaScript projects!
Here's the github repo with all the solutions of this challenge
Happy coding!