
Problem Statement
In this task, you are required to design an EventEmitter
class similar, yet distinct, from those used in Node.js and DOM's EventTarget interface. The primary purpose of this class is to facilitate the subscribing and emitting of events.
- Subscribe: Users should be able to register interest in specific events by subscribing to them. Each subscription can involve multiple listeners and the responses should be managed in the order they were initially registered.
- Emit: This function should enable the actual firing of an event, optionally passing arguments to the subscribed callbacks. If no subscriptions exist for an event, an empty response should be given.
This setup involves a straightforward interaction model where events can have multiple independent listeners, and upon triggering an event, every listener receives the appropriate data in a predictable sequence.
Examples
Example 1
Input:
actions = ["EventEmitter", "emit", "subscribe", "subscribe", "emit"], values = [[], ["firstEvent"], ["firstEvent", "function cb1() { return 5; }"], ["firstEvent", "function cb1() { return 6; }"], ["firstEvent"]]
Output:
[[],["emitted",[]],["subscribed"],["subscribed"],["emitted",[5,6]]]
Explanation:
const emitter = new EventEmitter(); emitter.emit("firstEvent"); // [], no callback are subscribed yet emitter.subscribe("firstEvent", function cb1() { return 5; }); emitter.subscribe("firstEvent", function cb2() { return 6; }); emitter.emit("firstEvent"); // [5, 6], returns the output of cb1 and cb2
Example 2
Input:
actions = ["EventEmitter", "subscribe", "emit", "emit"], values = [[], ["firstEvent", "function cb1(...args) { return args.join(','); }"], ["firstEvent", [1,2,3]], ["firstEvent", [3,4,6]]]
Output:
[[],["subscribed"],["emitted",["1,2,3"]],["emitted",["3,4,6"]]]
Explanation:
Note that the emit method should be able to accept an OPTIONAL array of arguments. const emitter = new EventEmitter(); emitter.subscribe("firstEvent, function cb1(...args) { return args.join(','); }); emitter.emit("firstEvent", [1, 2, 3]); // ["1,2,3"] emitter.emit("firstEvent", [3, 4, 6]); // ["3,4,6"]
Example 3
Input:
actions = ["EventEmitter", "subscribe", "emit", "unsubscribe", "emit"], values = [[], ["firstEvent", "(...args) => args.join(',')"], ["firstEvent", [1,2,3]], [0], ["firstEvent", [4,5,6]]]
Output:
[[],["subscribed"],["emitted",["1,2,3"]],["unsubscribed",0],["emitted",[]]]
Explanation:
const emitter = new EventEmitter(); const sub = emitter.subscribe("firstEvent", (...args) => args.join(',')); emitter.emit("firstEvent", [1, 2, 3]); // ["1,2,3"] sub.unsubscribe(); // undefined emitter.emit("firstEvent", [4, 5, 6]); // [], there are no subscriptions
Example 4
Input:
actions = ["EventEmitter", "subscribe", "subscribe", "unsubscribe", "emit"], values = [[], ["firstEvent", "x => x + 1"], ["firstEvent", "x => x + 2"], [0], ["firstEvent", [5]]]
Output:
[[],["subscribed"],["subscribed"],["unsubscribed",0],["emitted",[7]]]
Explanation:
const emitter = new EventEmitter(); const sub1 = emitter.subscribe("firstEvent", x => x + 1); const sub2 = emitter.subscribe("firstEvent", x => x + 2); sub1.unsubscribe(); // undefined emitter.emit("firstEvent", [5]); // [7]
Constraints
1 <= actions.length <= 10
values.length === actions.length
- All test cases are valid, e.g. you don't need to handle scenarios when unsubscribing from a non-existing subscription.
- There are only 4 different actions:
EventEmitter
,emit
,subscribe
, andunsubscribe
. - The
EventEmitter
action doesn't take any arguments. - The
emit
action takes between either 1 or 2 arguments. The first argument is the name of the event we want to emit, and the 2nd argument is passed to the callback functions. - The
subscribe
action takes 2 arguments, where the first one is the event name and the second is the callback function. - The
unsubscribe
action takes one argument, which is the 0-indexed order of the subscription made before.
Approach and Intuition
To tackle this problem, let's break down the process into two main functionalities detailed in the examples:
For every action of
EventEmitter
:- Create an instance of the EventEmitter class, which will manage all the event handling logic.
For
subscribe
actions:- Record the subscription with the event name and associated callback.
- Ensure that each callback can be unsubscribed individually without affecting other subscriptions to the same event.
For
emit
actions:- When an event is triggered, collect all the responses from callbacks subscribed to this event. These should be processed in the same order as they were added.
- If an event has arguments, pass them to each callback function.
- Handle cases where no subscriptions are active for an event and return an empty array.
For
unsubscribe
actions:- Allow a specific subscription (referenced by its position or a unique identifier) to be cancelled effectively, ensuring the internal lists of subscribers are updated accordingly.
By adhering to these principles, the EventEmitter class you design will mimic the functionality as described, processing and responding to internal events within your application or framework. These steps correspond to the intuitive process of observing and reacting to events, which is a staple in interactive, asynchronous programming environments.
Solutions
- JavaScript
class EventNotifier {
constructor() {
this.subscribers = {};
}
on(eventType, callback) {
if (!(eventType in this.subscribers)) {
this.subscribers[eventType] = new Set([callback]);
} else {
this.subscribers[eventType].add(callback);
}
return {
detach: () => {
this.subscribers[eventType].delete(callback);
},
};
}
notify(eventType, parameters = []) {
if (!(eventType in this.subscribers)) return [];
const outcomes = [];
this.subscribers[eventType].forEach((handler) => {
outcomes.push(handler(...parameters));
});
return outcomes;
}
}
The provided JavaScript code implements an EventNotifier
class tailored for event handling. This class facilitates a subscription model where callbacks are registered to specific event types and can be notified when those events occur. Here's a breakdown of its functionalities:
Initialization: The
EventNotifier
class initializes with asubscribers
object that keeps track of events and their corresponding listeners (callbacks).Event Subscription (
on
method):- Register a callback for a specified event type.
- If the event type does not already exist in the
subscribers
object, create a newSet
containing the callback; otherwise, add the callback to the existing set for that event type. - Returns an object with a
detach
method, allowing for the removal of the callback from the event type.
Event Notification (
notify
method):- Trigger callbacks associated with a specified event type.
- If the event type does not exist in the
subscribers
object, return an empty array as there are no subscribers to notify. - For each registered callback, execute the callback passing any provided parameters and collect the return values into an array.
This system is efficient for scenarios where components or services within an application require notification of events to perform actions, acting as a core mechanism for implementing observer patterns in applications.
No comments yet.