Event subscriber
Challenge 10 - Medium
Description
Create an event subscriber that lets you subscribe to events and run a callback when the event is triggered. You should be able to subscribe to an event by its name, specify how many times it can be called, and provide a callback for each call. When the event is triggered, the callback should run, and the count should decrease. Once the count reaches 0, the promise returned by the subscription should resolve.
Requirements:
- Save events by name, the number of times they can be called, and a callback for each call.
- On subscription, the function should return a promise that will be resolved when the count reaches 0.
- Execute the callback when the event is triggered.
- Remove events when the count reaches 0 or if the remove function is executed.
1. Class - Implementation
The class definition
The class will have a registry that is a map of the events that are subscribed. We want to initialize it on the constructor to avoid any issues with the registry being undefined.
0
1
2
3
4
5
6
7
8
export class EventSubscriber {
registry: Map<string, SubscriptionEvent>;
constructor() {
this.registry = new Map<string, SubscriptionEvent>();
}
}
The registry type
The type is going to have the name of the event, the callback that should execute, the amount of times that can be called and a promise that resolves when the callback is executed.
0
1
2
3
4
5
6
7
type SubscriptionEvent = {
name: string;
callback: Function;
resolve: (name: string) => void;
promise: Promise<string>;
amount: number;
}
2. Methods - Implementation
The class is going to have 3 main methods that are going to define the whole behavior of the event subscriber.
Registration method
The on method is going to register a new event in the registry. The interface of the method is going to have it's name, the callback that needs to execute after is emited and the amount of times that can be executed. When is called is going to check if the event is already registered and if not, it will add it to the registry. It will return a promise that will be resolved when the count reaches 0. In order to do this, we are going to save the resolve function in the event object.
There are multiple soultions to this and only adding the event if is not in the registry is a assumption, the functionality could be to add the amount in the number to an already registered event with that name, could be assigning another one with the same name that can be executed the same amount of times (maybe with another callback), etc. In this case, we are going to add a new event if is not in the registry.
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
on(name: string, callback: Function, amount: number): Promise<string> {
const subscription = this.registry.get(name);
let functionToResolve: (name: string) => void;
const promise = new Promise<string>((resolve) => {
functionToResolve = resolve;
});
if(!subscription) {
console.log(`Adding new event --> ${name} that can be called ${amount} times`);
this.registry.set(name, {
name: name,
callback: callback,
resolve: functionToResolve!,
amount: amount,
promise: promise,
} as SubscriptionEvent);
return promise;
}
return subscription.promise
}
Emit method
The emit method is used to trigger the event and call the callback function associated with it. It will check if the event is in the registry and if it is, it will execute the callback and decrease the amount of times that can be called. If the amount reaches 0, it will resolve the promise and remove the event from the registry.
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
emit(name: string) {
const subscription = this.registry.get(name);
if(subscription) {
if(subscription.amount > 0) {
console.log(`Emiting --> ${subscription.name} --> ${subscription.amount -1} times left to call`);
subscription.callback();
subscription.amount--;
}
if(subscription.amount === 0 ) {
console.log(`Amount for ${subscription.name} is zero.`);
subscription.resolve(`${subscription.name} has been resolved`);
this.remove(name);
}
}
}
Remove method
The remove method is going to remove an event from the registry.
0
1
2
3
4
remove(name: string) {
console.log(`Removing --> ${name}`);
this.registry.delete(name);
}
3. Code Testing
What to expect
Open the console and you should see something like this.
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Adding new event --> Ale that can be called 3 times
Adding new event --> Seba that can be called 1 times
Emiting --> Ale --> 2 times left to call
Ale event callback
Emiting --> Ale --> 1 times left to call
Ale event callback
Emiting --> Seba --> 0 times left to call
Seba event callback
Amount for Seba is zero.
Removing --> Seba
Emiting --> Ale --> 0 times left to call
Ale event callback
Amount for Ale is zero.
Removing --> Ale
Ale has been resolved
Seba has been resolved
4. Complete Implementation
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
type SubscriptionEvent = {
name: string;
callback: Function;
resolve: (name: string) => void;
promise: Promise<string>;
amount: number;
}
export class EventSubscriber {
registry: Map<string, SubscriptionEvent>;
constructor() {
this.registry = new Map<string, SubscriptionEvent>();
}
on(name: string, callback: Function, amount: number): Promise<string> {
const subscription = this.registry.get(name);
let functionToResolve: (name: string) => void;
const promise = new Promise<string>((resolve) => {
functionToResolve = resolve;
});
if(!subscription) {
console.log(`Adding new event --> ${name} that can be called ${amount} times`);
this.registry.set(name, {
name: name,
callback: callback,
resolve: functionToResolve!,
amount: amount,
promise: promise,
} as SubscriptionEvent);
return promise;
}
return subscription.promise
}
emit(name: string) {
const subscription = this.registry.get(name);
if(subscription) {
if(subscription.amount > 0) {
console.log(`Emiting --> ${subscription.name} --> ${subscription.amount -1} times left to call`);
subscription.callback();
subscription.amount--;
}
if(subscription.amount === 0 ) {
console.log(`Amount for ${subscription.name} is zero.`);
subscription.resolve(`${subscription.name} has been resolved`);
this.remove(name);
}
}
}
remove(name: string) {
console.log(`Removing --> ${name}`);
this.registry.delete(name);
}
}