-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathMinEventEmitter.ts
More file actions
102 lines (86 loc) · 3.11 KB
/
MinEventEmitter.ts
File metadata and controls
102 lines (86 loc) · 3.11 KB
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import SplitIO from '../../types/splitio';
const NEW_LISTENER_EVENT = 'newListener';
const REMOVE_LISTENER_EVENT = 'removeListener';
function checkListener(listener: unknown) {
if (typeof listener !== 'function') {
throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
}
}
// @TODO implement missing methods, check spec and add UTs
export class EventEmitter implements SplitIO.IEventEmitter {
private listeners: Record<string, Array<[
(...args: any[]) => void, // the event listener
boolean // whether it is a one-time listener or not
]>> = {};
private registerListener(type: string, listener: (...args: any[]) => void, oneTime: boolean) {
checkListener(listener);
// To avoid recursion in the case that type === "newListener" before
// adding it to the listeners, first emit "newListener".
this.emit(NEW_LISTENER_EVENT, type, listener);
if (!this.listeners[type]) {
this.listeners[type] = [[listener, oneTime]];
} else {
this.listeners[type].push([listener, oneTime]);
}
return this;
}
addListener(type: string, listener: (...args: any[]) => void) {
return this.registerListener(type, listener, false);
}
// alias of addListener
on(type: string, listener: (...args: any[]) => void) {
return this.addListener(type, listener);
}
once(type: string, listener: (...args: any[]) => void) {
return this.registerListener(type, listener, true);
}
// @ts-ignore
removeListener(/* type: string, listener: (...args: any[]) => void */) {
throw new Error('Method not implemented.');
}
// @ts-ignore alias of removeListener
off(/* type: string, listener: (...args: any[]) => void */) {
return this.removeListener(/* type, listener */);
}
emit(type: string, ...args: any[]): boolean {
// Returns false if the event doesn't have listeners
if (!this.listeners[type] || this.listeners[type].length === 0) return false;
// Call listeners while removing one-time listeners
this.listeners[type] = this.listeners[type].filter(listenerEntry => {
listenerEntry[0](...args);
return !listenerEntry[1];
});
return true;
}
removeAllListeners(type?: string) {
if (!this.listeners[REMOVE_LISTENER_EVENT]) {
// if not listening for `removeListener`, no need to emit
if (type) {
if (this.listeners[type]) delete this.listeners[type];
} else {
this.listeners = {};
}
} else {
// emit `removeListener` for all listeners
if (type) {
const listeners = this.listeners[type];
if (listeners) {
// LIFO order
for (let i = listeners.length - 1; i >= 0; i--) {
this.emit(REMOVE_LISTENER_EVENT, type, listeners[i]);
}
delete this.listeners[type];
}
} else {
const keys = Object.keys(this.listeners);
for (let i = 0; i < keys.length; ++i) {
const key = keys[i];
if (key === REMOVE_LISTENER_EVENT) continue;
this.removeAllListeners(key);
}
this.listeners = {};
}
}
return this;
}
}