1
// Subscribe -> needs to return a release function so subscriber no longer receives event
2
// Thinking of a map of eventName: -> callbackArray so we can have multiple subscribers to the same event
3
// Emit -> lookup the event name in the map -> loop through callbackArray and relay args to each callback call
4
// Release -> need to remove the subscriber from the event's callbackArray
5
class EventEmitter {
6
constructor() {
7
// Initialize an event -> callbackArray map
8
this.eventMap = {};
9
}
10
11
subscribe(eventName, callback) {
12
// We wrap the callback in another function that returns the callback so we have a new reference that will help us with filtering the proper subscription in the release function
13
// even if there are multiple subscriptions using the same callback1 for example
14
const subscribedCallback = () => callback;
15
// If there is an existing eventName to subscribe to, push the callback onto the array
16
if (this.eventMap[eventName]) {
17
this.eventMap[eventName].push(subscribedCallback);
18
} else {
19
// Otherwise, we initialize it with a new array with the 1 callback inside
20
this.eventMap[eventName] = [subscribedCallback];
21
}
22
23
// Need to return an object with a release function that will remove the callback from the event's array
24
// We have closure around the subscribedCallback reference so we can filter out the exact subscription later
25
const self = this;
26
return {
27
release: function release() {
28
if (self.eventMap[eventName]) {
29
// Remove the subscribedCallback from the event's callback array
30
self.eventMap[eventName] = self.eventMap[eventName].filter((currentCallback) => subscribedCallback !== currentCallback);
31
32
// If there are no more callbacks subscribed to the event, we can remove the event from the map
33
if (self.eventMap[eventName].length === 0) {
34
delete self.eventMap[eventName];
35
}
36
}
37
}
38
}
39
}
40
41
emit(eventName, ...args) {
42
// If the event doesn't exist in the map, do nothing
43
if (!this.eventMap[eventName]) {
44
return;
45
}
46
47
// Otherwise, we will loop through the event's callback array and apply the callbacks with relayed args
48
this.eventMap[eventName].forEach((subscribedCallback) => {
49
const currentCallback = subscribedCallback();
50
currentCallback.apply(this, args);
51
});
52
}
53
}
54
55
const emitter = new EventEmitter();
56
57
const callback1 = (...args) => { console.log(`Callback 1 ${args}`); };
58
const callback2 = () => { console.log("Callback2"); };
59
const sub1 = emitter.subscribe('event1', callback1);
60
const sub2 = emitter.subscribe('event2', callback2);
61
62
emitter.emit("event1", 1, 2);
63
emitter.emit("event2");
64
65
// same callback could subscribe
66
// on same event multiple times
67
const sub3 = emitter.subscribe('event1', callback1);
68
69
emitter.emit("event1", 1,2,3);
70
71
console.log("Releasing sub1");
72
sub1.release();
73
emitter.emit("event1", 4, 5);
74
console.log("Releasing sub3");
75
sub3.release();
76
emitter.emit("event1", 5, 6);
77
console.log("Releasing sub2");
78
sub2.release();
79
emitter.emit("event2");