@supercat1337/event-emitter 1.0.10 → 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +194 -65
- package/package.json +29 -29
- package/src/index.js +139 -32
- package/tests/test.js +50 -60
package/README.md
CHANGED
|
@@ -1,92 +1,221 @@
|
|
|
1
|
-
# event-emitter
|
|
1
|
+
# @supercat1337/event-emitter 🐈⚡
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A modern, feature-rich EventEmitter implementation for JavaScript and TypeScript with advanced capabilities and excellent type safety.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
You can define types for the events that you want to emit and listen to.
|
|
5
|
+
## Features
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
- ✅ **Full TypeScript support** with generics and complete type definitions
|
|
8
|
+
- 🎯 **Promise-based event waiting** with timeout support
|
|
9
|
+
- 📊 **Listener lifecycle tracking** - know when events gain/lose listeners
|
|
10
|
+
- 🛡️ **Memory safe** - automatic cleanup and unsubscribe functions
|
|
11
|
+
- ⚡ **High performance** - optimized for frequent events
|
|
12
|
+
- 🔒 **Immutable patterns** - listener arrays are copied during emission
|
|
13
|
+
- 🚀 **Modern ES2022+** - private fields, arrow functions, and more
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @supercat1337/event-emitter
|
|
11
19
|
```
|
|
12
20
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
- removeListener(event, listener) - Remove an event listener.
|
|
18
|
-
- waitForEvent(event, max_wait_ms = 0) - Wait for an event to be emitted. If max_wait_ms is set to 0, the function will wait indefinitely.
|
|
19
|
-
- waitForAnyEvent(events, max_wait_ms = 0) - Wait for any of the specified events to be emitted. If max_wait_ms is set to 0, the function will wait indefinitely.
|
|
20
|
-
- clear() - Remove all event listeners
|
|
21
|
-
- clearEventListeners(event) - Remove all listeners for a specified event
|
|
22
|
-
- onHasEventListeners(callback) - Subscribe to the "#has-listeners" event. This event is emitted when the number of listeners for any event (except "#has-listeners" and "#no-listeners") goes from 0 to 1.
|
|
23
|
-
- onNoEventListeners(callback) - Subscribe to the "#no-listeners" event. This event is emitted when the number of listeners for any event (except "#has-listeners" and "#no-listeners") goes from 1 to 0.
|
|
24
|
-
|
|
25
|
-
### Usage
|
|
26
|
-
|
|
27
|
-
Basic example
|
|
28
|
-
```js
|
|
29
|
-
import { EventEmitter } from "@supercat1337/event-emitter";
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```javascript
|
|
24
|
+
import { EventEmitter } from '@supercat1337/event-emitter';
|
|
30
25
|
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
var ev = new EventEmitter();
|
|
26
|
+
// Define your event types
|
|
27
|
+
type AppEvents = 'user:created' | 'user:deleted' | 'notification:sent';
|
|
34
28
|
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
const emitter = new EventEmitter<AppEvents>();
|
|
30
|
+
|
|
31
|
+
// Subscribe to events
|
|
32
|
+
const unsubscribe = emitter.on('user:created', (userData) => {
|
|
33
|
+
console.log('User created:', userData);
|
|
37
34
|
});
|
|
38
35
|
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
// Emit events
|
|
37
|
+
emitter.emit('user:created', { id: 1, name: 'John' });
|
|
38
|
+
|
|
39
|
+
// Unsubscribe when done
|
|
40
|
+
unsubscribe();
|
|
42
41
|
```
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
## API Reference
|
|
44
|
+
|
|
45
|
+
### Core Methods
|
|
47
46
|
|
|
48
|
-
|
|
49
|
-
var ev = new EventEmitter;
|
|
50
|
-
var foo = 0
|
|
51
|
-
var action = () => {
|
|
52
|
-
foo++;
|
|
53
|
-
};
|
|
47
|
+
#### `on(event, listener)`
|
|
54
48
|
|
|
55
|
-
|
|
49
|
+
Subscribe to an event. Returns an unsubscribe function.
|
|
56
50
|
|
|
57
|
-
|
|
58
|
-
|
|
51
|
+
```javascript
|
|
52
|
+
const unsubscribe = emitter.on("user:created", (data) => {
|
|
53
|
+
console.log(data);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Later...
|
|
57
|
+
unsubscribe();
|
|
58
|
+
```
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
#### `off(event, listener)`
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
Remove a specific listener from an event.
|
|
63
|
+
|
|
64
|
+
```javascript
|
|
65
|
+
function listener(data) {
|
|
66
|
+
/* ... */
|
|
66
67
|
}
|
|
68
|
+
emitter.on("user:created", listener);
|
|
69
|
+
emitter.off("user:created", listener);
|
|
67
70
|
```
|
|
68
71
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
#### `emit(event, ...args)`
|
|
73
|
+
|
|
74
|
+
Emit an event with optional arguments.
|
|
75
|
+
|
|
76
|
+
```javascript
|
|
77
|
+
emitter.emit("user:created", { id: 1, name: "Alice" });
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
#### `once(event, listener)`
|
|
72
81
|
|
|
73
|
-
|
|
74
|
-
var ev = new EventEmitter;
|
|
82
|
+
Add a one-time listener that auto-removes after execution.
|
|
75
83
|
|
|
76
|
-
|
|
84
|
+
```javascript
|
|
85
|
+
emitter.once("app:ready", () => {
|
|
86
|
+
console.log("App is ready!");
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Advanced Methods
|
|
91
|
+
|
|
92
|
+
#### `waitForEvent(event, [maxWaitMs])`
|
|
93
|
+
|
|
94
|
+
Wait for an event using Promises.
|
|
95
|
+
|
|
96
|
+
```javascript
|
|
97
|
+
// Wait indefinitely
|
|
98
|
+
const result = await emitter.waitForEvent("data:loaded");
|
|
99
|
+
|
|
100
|
+
// Wait with timeout (returns false if timeout reached)
|
|
101
|
+
const success = await emitter.waitForEvent("data:loaded", 5000);
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
#### `waitForAnyEvent(events, [maxWaitMs])`
|
|
77
105
|
|
|
78
|
-
|
|
79
|
-
|
|
106
|
+
Wait for any of multiple events.
|
|
107
|
+
|
|
108
|
+
```javascript
|
|
109
|
+
const events = ['success', 'error', 'timeout'] as const;
|
|
110
|
+
const result = await emitter.waitForAnyEvent(events, 3000);
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
#### `onHasEventListeners(callback)`
|
|
114
|
+
|
|
115
|
+
Get notified when any event gains its first listener.
|
|
116
|
+
|
|
117
|
+
```javascript
|
|
118
|
+
emitter.onHasEventListeners((eventName) => {
|
|
119
|
+
console.log(`Event ${eventName} now has listeners!`);
|
|
80
120
|
});
|
|
121
|
+
```
|
|
81
122
|
|
|
82
|
-
|
|
83
|
-
ev.emit("foo");
|
|
84
|
-
ev.emit("foo");
|
|
123
|
+
#### `onNoEventListeners(callback)`
|
|
85
124
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
125
|
+
Get notified when any event loses its last listener.
|
|
126
|
+
|
|
127
|
+
```javascript
|
|
128
|
+
emitter.onNoEventListeners((eventName) => {
|
|
129
|
+
console.log(`Event ${eventName} has no more listeners!`);
|
|
130
|
+
});
|
|
91
131
|
```
|
|
92
132
|
|
|
133
|
+
### Lifecycle Management
|
|
134
|
+
|
|
135
|
+
#### `destroy()`
|
|
136
|
+
|
|
137
|
+
Completely destroy the emitter and clean up all resources.
|
|
138
|
+
|
|
139
|
+
```javascript
|
|
140
|
+
emitter.destroy();
|
|
141
|
+
console.log(emitter.isDestroyed); // true
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
#### `clear()`
|
|
145
|
+
|
|
146
|
+
Remove all listeners while keeping the emitter functional.
|
|
147
|
+
|
|
148
|
+
```javascript
|
|
149
|
+
emitter.clear();
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
#### `clearEventListeners(event)`
|
|
153
|
+
|
|
154
|
+
Remove all listeners for a specific event.
|
|
155
|
+
|
|
156
|
+
```javascript
|
|
157
|
+
emitter.clearEventListeners("user:created");
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## TypeScript Usage
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
import { EventEmitter } from "@supercat1337/event-emitter";
|
|
164
|
+
|
|
165
|
+
// Define your event types
|
|
166
|
+
type MyEvents =
|
|
167
|
+
| "user:created"
|
|
168
|
+
| "user:updated"
|
|
169
|
+
| { type: "user:deleted"; payload: { id: string; reason: string } };
|
|
170
|
+
|
|
171
|
+
const emitter = new EventEmitter<MyEvents>();
|
|
172
|
+
|
|
173
|
+
// Full type safety!
|
|
174
|
+
emitter.emit("user:created", { id: 1, name: "John" }); // ✅ Correct
|
|
175
|
+
emitter.emit("user:created", "invalid"); // ❌ Type error
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Error Handling
|
|
179
|
+
|
|
180
|
+
All listener errors are caught and logged to console, preventing emitter crashes:
|
|
181
|
+
|
|
182
|
+
```javascript
|
|
183
|
+
emitter.on("data:received", () => {
|
|
184
|
+
throw new Error("Something went wrong!");
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// Error is caught and logged, emitter continues working
|
|
188
|
+
emitter.emit("data:received");
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Performance Notes
|
|
192
|
+
|
|
193
|
+
- 🔄 Listener arrays are copied before iteration to allow safe modification during emission
|
|
194
|
+
- ⚡ Event existence checks are optimized with direct property access
|
|
195
|
+
- 🗑️ Automatic cleanup prevents memory leaks
|
|
196
|
+
- 📏 No external dependencies
|
|
197
|
+
|
|
198
|
+
## Browser Support
|
|
199
|
+
|
|
200
|
+
| Feature | Support |
|
|
201
|
+
| ---------- | --------------- |
|
|
202
|
+
| ES2022+ | Modern browsers |
|
|
203
|
+
| TypeScript | 4.0+ |
|
|
204
|
+
| Node.js | 14+ |
|
|
205
|
+
|
|
206
|
+
## License
|
|
207
|
+
|
|
208
|
+
MIT License - feel free to use in commercial projects.
|
|
209
|
+
|
|
210
|
+
## Contributing
|
|
211
|
+
|
|
212
|
+
Contributions welcome! Please ensure:
|
|
213
|
+
|
|
214
|
+
- ✅ All tests pass
|
|
215
|
+
- ✅ TypeScript types are maintained
|
|
216
|
+
- ✅ New features include tests
|
|
217
|
+
- ✅ Code follows existing style
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
**Made with ❤️ by [supercat1337](https://github.com/supercat1337)**
|
package/package.json
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
2
|
+
"name": "@supercat1337/event-emitter",
|
|
3
|
+
"version": "1.0.11",
|
|
4
|
+
"description": "Event Emitter",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "c8 ava",
|
|
8
|
+
"build": "npm run remove_type_files && npm run build_esm && npm run build_esm_min && npm run create_types",
|
|
9
|
+
"build_esm": "rollup ./src/index.js --file ./dist/event-emitter.esm.js --format es",
|
|
10
|
+
"build_esm_min": "esbuild --minify --bundle --platform=neutral --legal-comments=none ./src/index.js --outfile=./dist/event-emitter.esm.min.js",
|
|
11
|
+
"create_types": "npx -p typescript tsc --project my.tsconfig.types.json",
|
|
12
|
+
"remove_type_files": "del /q *.d.ts *.d.ts.map && cd dist && del /s /q *.d.ts *.d.ts.map && cd ../src && del /s /q *.d.ts *.d.ts.map && cd .."
|
|
13
13
|
},
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
14
|
+
"author": "Supercat",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"ava": "^6.1.2",
|
|
18
|
+
"c8": "^9.1.0",
|
|
19
|
+
"esbuild": "^0.20.2",
|
|
20
|
+
"@types/node": "^20.12.2"
|
|
21
|
+
},
|
|
22
|
+
"type": "module",
|
|
23
|
+
"moduleResolution": "nodenext",
|
|
24
|
+
"keywords": [
|
|
25
|
+
"typed event emitter",
|
|
26
|
+
"typed event bus"
|
|
27
|
+
],
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"registry": "https://registry.npmjs.org"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://github.com/supercat1337/event-emitter"
|
|
32
32
|
}
|
package/src/index.js
CHANGED
|
@@ -8,15 +8,31 @@ class EventEmitter {
|
|
|
8
8
|
/** @type {Object.<string, Function[]>} */
|
|
9
9
|
events = {};
|
|
10
10
|
|
|
11
|
+
/** @type {Object.<"#has-listeners"|"#no-listeners", Function[]>} */
|
|
12
|
+
#internalEvents = { "#has-listeners": [], "#no-listeners": [] };
|
|
13
|
+
|
|
14
|
+
#isDestroyed = false;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Is the event emitter destroyed?
|
|
18
|
+
* @type {boolean}
|
|
19
|
+
*/
|
|
20
|
+
get isDestroyed() {
|
|
21
|
+
return this.#isDestroyed;
|
|
22
|
+
}
|
|
23
|
+
|
|
11
24
|
/**
|
|
12
25
|
* on is used to add a callback function that's going to be executed when the event is triggered
|
|
13
|
-
* @param {T
|
|
26
|
+
* @param {T} event
|
|
14
27
|
* @param {Function} listener
|
|
15
28
|
* @returns {()=>void}
|
|
16
29
|
*/
|
|
17
30
|
on(event, listener) {
|
|
31
|
+
if (this.#isDestroyed) {
|
|
32
|
+
throw new Error("EventEmitter is destroyed");
|
|
33
|
+
}
|
|
18
34
|
|
|
19
|
-
if (typeof this.events[event] !==
|
|
35
|
+
if (typeof this.events[event] !== "object") {
|
|
20
36
|
this.events[event] = [];
|
|
21
37
|
}
|
|
22
38
|
|
|
@@ -28,61 +44,141 @@ class EventEmitter {
|
|
|
28
44
|
that.removeListener(event, listener);
|
|
29
45
|
};
|
|
30
46
|
|
|
31
|
-
if (
|
|
32
|
-
this
|
|
47
|
+
if (this.events[event].length == 1) {
|
|
48
|
+
this.#emitInternal("#has-listeners", event);
|
|
33
49
|
}
|
|
34
50
|
|
|
35
51
|
return unsubscriber;
|
|
36
52
|
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Internal method to add a listener to an internal event
|
|
56
|
+
* @param {"#has-listeners"|"#no-listeners"} event
|
|
57
|
+
* @param {Function} listener
|
|
58
|
+
* @returns {()=>void}
|
|
59
|
+
*/
|
|
60
|
+
#onInternalEvent(event, listener) {
|
|
61
|
+
if (typeof this.#internalEvents[event] !== "object") {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
this.#internalEvents[event].push(listener);
|
|
65
|
+
|
|
66
|
+
let that = this;
|
|
67
|
+
|
|
68
|
+
let unsubscriber = function () {
|
|
69
|
+
that.#removeInternalListener(event, listener);
|
|
70
|
+
};
|
|
71
|
+
return unsubscriber;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Internal method to remove a listener from an internal event
|
|
76
|
+
* @param {"#has-listeners"|"#no-listeners"} event
|
|
77
|
+
* @param {Function} listener
|
|
78
|
+
*/
|
|
79
|
+
#removeInternalListener(event, listener) {
|
|
80
|
+
var idx;
|
|
81
|
+
|
|
82
|
+
if (typeof this.#internalEvents[event] === "object") {
|
|
83
|
+
idx = this.#internalEvents[event].indexOf(listener);
|
|
84
|
+
|
|
85
|
+
if (idx > -1) {
|
|
86
|
+
this.#internalEvents[event].splice(idx, 1);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* off is an alias for removeListener
|
|
93
|
+
* @param {T} event
|
|
94
|
+
* @param {Function} listener
|
|
95
|
+
*/
|
|
96
|
+
off(event, listener) {
|
|
97
|
+
return this.removeListener(event, listener);
|
|
98
|
+
}
|
|
99
|
+
|
|
37
100
|
/**
|
|
38
101
|
* Remove an event listener from an event
|
|
39
|
-
* @param {T
|
|
102
|
+
* @param {T} event
|
|
40
103
|
* @param {Function} listener
|
|
41
104
|
*/
|
|
42
105
|
removeListener(event, listener) {
|
|
106
|
+
if (this.#isDestroyed) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
43
109
|
var idx;
|
|
44
110
|
|
|
45
|
-
if (
|
|
46
|
-
|
|
111
|
+
if (!this.events[event]) return;
|
|
112
|
+
idx = this.events[event].indexOf(listener);
|
|
47
113
|
|
|
48
|
-
|
|
49
|
-
|
|
114
|
+
if (idx > -1) {
|
|
115
|
+
this.events[event].splice(idx, 1);
|
|
50
116
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
117
|
+
if (this.events[event].length == 0) {
|
|
118
|
+
this.#emitInternal("#no-listeners", event);
|
|
54
119
|
}
|
|
55
120
|
}
|
|
56
|
-
|
|
57
121
|
}
|
|
122
|
+
|
|
58
123
|
/**
|
|
59
124
|
* emit is used to trigger an event
|
|
60
|
-
* @param {T
|
|
125
|
+
* @param {T} event
|
|
61
126
|
*/
|
|
62
127
|
emit(event) {
|
|
63
|
-
if (
|
|
128
|
+
if (this.#isDestroyed) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (typeof this.events[event] !== "object") return;
|
|
64
133
|
|
|
65
|
-
var i,
|
|
134
|
+
var i,
|
|
135
|
+
listeners,
|
|
136
|
+
length,
|
|
137
|
+
args = [].slice.call(arguments, 1);
|
|
66
138
|
|
|
67
139
|
listeners = this.events[event].slice();
|
|
68
140
|
length = listeners.length;
|
|
69
141
|
|
|
70
142
|
for (i = 0; i < length; i++) {
|
|
71
|
-
|
|
72
143
|
try {
|
|
73
144
|
listeners[i].apply(this, args);
|
|
74
|
-
}
|
|
75
|
-
catch (e) {
|
|
145
|
+
} catch (e) {
|
|
76
146
|
console.error(event, args);
|
|
77
147
|
console.error(e);
|
|
78
148
|
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Internal function to emit an event
|
|
154
|
+
* @param {"#has-listeners"|"#no-listeners"} event
|
|
155
|
+
* @param {...any} args
|
|
156
|
+
*/
|
|
157
|
+
#emitInternal(event, ...args) {
|
|
158
|
+
if (this.#isDestroyed) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
79
161
|
|
|
162
|
+
if (typeof this.#internalEvents[event] !== "object") return;
|
|
163
|
+
|
|
164
|
+
var i, listeners, length;
|
|
165
|
+
|
|
166
|
+
listeners = this.#internalEvents[event].slice();
|
|
167
|
+
length = listeners.length;
|
|
168
|
+
|
|
169
|
+
for (i = 0; i < length; i++) {
|
|
170
|
+
try {
|
|
171
|
+
listeners[i].apply(this, args);
|
|
172
|
+
} catch (e) {
|
|
173
|
+
console.error(event, args);
|
|
174
|
+
console.error(e);
|
|
175
|
+
}
|
|
80
176
|
}
|
|
81
177
|
}
|
|
82
178
|
|
|
83
179
|
/**
|
|
84
180
|
* Add a one-time listener
|
|
85
|
-
* @param {T
|
|
181
|
+
* @param {T} event
|
|
86
182
|
* @param {Function} listener
|
|
87
183
|
* @returns {()=>void}
|
|
88
184
|
*/
|
|
@@ -93,7 +189,6 @@ class EventEmitter {
|
|
|
93
189
|
});
|
|
94
190
|
}
|
|
95
191
|
|
|
96
|
-
|
|
97
192
|
/**
|
|
98
193
|
* Wait for an event to be emitted
|
|
99
194
|
* @param {T} event
|
|
@@ -101,12 +196,14 @@ class EventEmitter {
|
|
|
101
196
|
* @returns {Promise<boolean>} - Resolves with true if the event was emitted, false if the time ran out.
|
|
102
197
|
*/
|
|
103
198
|
waitForEvent(event, max_wait_ms = 0) {
|
|
199
|
+
if (this.#isDestroyed) {
|
|
200
|
+
throw new Error("EventEmitter is destroyed");
|
|
201
|
+
}
|
|
104
202
|
|
|
105
203
|
return new Promise((resolve) => {
|
|
106
204
|
let timeout;
|
|
107
205
|
|
|
108
206
|
let unsubscriber = this.on(event, () => {
|
|
109
|
-
|
|
110
207
|
if (max_wait_ms > 0) {
|
|
111
208
|
clearTimeout(timeout);
|
|
112
209
|
}
|
|
@@ -120,13 +217,10 @@ class EventEmitter {
|
|
|
120
217
|
unsubscriber();
|
|
121
218
|
resolve(false);
|
|
122
219
|
}, max_wait_ms);
|
|
123
|
-
|
|
124
220
|
}
|
|
125
|
-
|
|
126
221
|
});
|
|
127
222
|
}
|
|
128
223
|
|
|
129
|
-
|
|
130
224
|
/**
|
|
131
225
|
* Wait for any of the specified events to be emitted
|
|
132
226
|
* @param {T[]} events - Array of event names to wait for
|
|
@@ -134,6 +228,9 @@ class EventEmitter {
|
|
|
134
228
|
* @returns {Promise<boolean>} - Resolves with true if any event was emitted, false if the time ran out.
|
|
135
229
|
*/
|
|
136
230
|
waitForAnyEvent(events, max_wait_ms = 0) {
|
|
231
|
+
if (this.#isDestroyed) {
|
|
232
|
+
throw new Error("EventEmitter is destroyed");
|
|
233
|
+
}
|
|
137
234
|
|
|
138
235
|
return new Promise((resolve) => {
|
|
139
236
|
let timeout;
|
|
@@ -162,9 +259,7 @@ class EventEmitter {
|
|
|
162
259
|
main_unsubscriber();
|
|
163
260
|
resolve(false);
|
|
164
261
|
}, max_wait_ms);
|
|
165
|
-
|
|
166
262
|
}
|
|
167
|
-
|
|
168
263
|
});
|
|
169
264
|
}
|
|
170
265
|
|
|
@@ -180,12 +275,18 @@ class EventEmitter {
|
|
|
180
275
|
* @alias clear
|
|
181
276
|
*/
|
|
182
277
|
destroy() {
|
|
183
|
-
this
|
|
278
|
+
if (this.#isDestroyed) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
this.#isDestroyed = true;
|
|
283
|
+
this.#internalEvents = { "#has-listeners": [], "#no-listeners": [] };
|
|
284
|
+
this.events = {};
|
|
184
285
|
}
|
|
185
286
|
|
|
186
287
|
/**
|
|
187
288
|
* Clears all listeners for a specified event.
|
|
188
|
-
* @param {T
|
|
289
|
+
* @param {T} event - The event for which to clear all listeners.
|
|
189
290
|
*/
|
|
190
291
|
clearEventListeners(event) {
|
|
191
292
|
let listeners = this.events[event] || [];
|
|
@@ -193,7 +294,7 @@ class EventEmitter {
|
|
|
193
294
|
|
|
194
295
|
if (listenersCount > 0) {
|
|
195
296
|
this.events[event] = [];
|
|
196
|
-
this
|
|
297
|
+
this.#emitInternal("#no-listeners", event);
|
|
197
298
|
}
|
|
198
299
|
}
|
|
199
300
|
|
|
@@ -203,7 +304,10 @@ class EventEmitter {
|
|
|
203
304
|
* @returns {()=>void}
|
|
204
305
|
*/
|
|
205
306
|
onHasEventListeners(callback) {
|
|
206
|
-
|
|
307
|
+
if (this.#isDestroyed) {
|
|
308
|
+
throw new Error("EventEmitter is destroyed");
|
|
309
|
+
}
|
|
310
|
+
return this.#onInternalEvent("#has-listeners", callback);
|
|
207
311
|
}
|
|
208
312
|
|
|
209
313
|
/**
|
|
@@ -212,7 +316,10 @@ class EventEmitter {
|
|
|
212
316
|
* @returns {()=>void}
|
|
213
317
|
*/
|
|
214
318
|
onNoEventListeners(callback) {
|
|
215
|
-
|
|
319
|
+
if (this.#isDestroyed) {
|
|
320
|
+
throw new Error("EventEmitter is destroyed");
|
|
321
|
+
}
|
|
322
|
+
return this.#onInternalEvent("#no-listeners", callback);
|
|
216
323
|
}
|
|
217
324
|
}
|
|
218
325
|
|
package/tests/test.js
CHANGED
|
@@ -4,23 +4,21 @@
|
|
|
4
4
|
import { EventEmitter } from "./../src/index.js";
|
|
5
5
|
import test from "./../node_modules/ava/entrypoints/main.mjs";
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
test("on(), emit()", t => {
|
|
7
|
+
test("on(), emit()", (t) => {
|
|
9
8
|
/** @type {EventEmitter<"foo"|"bar">} */
|
|
10
|
-
let ev = new EventEmitter;
|
|
9
|
+
let ev = new EventEmitter();
|
|
11
10
|
ev.on("foo", () => {
|
|
12
11
|
t.pass();
|
|
13
12
|
});
|
|
14
13
|
|
|
15
14
|
ev.emit("bar");
|
|
16
15
|
ev.emit("foo");
|
|
17
|
-
|
|
18
16
|
});
|
|
19
17
|
|
|
20
|
-
test("once(), emit()", t => {
|
|
18
|
+
test("once(), emit()", (t) => {
|
|
21
19
|
/** @type {EventEmitter<"foo">} */
|
|
22
|
-
let ev = new EventEmitter;
|
|
23
|
-
let foo = 0
|
|
20
|
+
let ev = new EventEmitter();
|
|
21
|
+
let foo = 0;
|
|
24
22
|
ev.once("foo", () => {
|
|
25
23
|
foo++;
|
|
26
24
|
});
|
|
@@ -32,14 +30,14 @@ test("once(), emit()", t => {
|
|
|
32
30
|
if (foo == 1) {
|
|
33
31
|
t.pass();
|
|
34
32
|
} else {
|
|
35
|
-
t.fail()
|
|
33
|
+
t.fail();
|
|
36
34
|
}
|
|
37
35
|
});
|
|
38
36
|
|
|
39
|
-
test("removeListener()", t => {
|
|
37
|
+
test("removeListener()", (t) => {
|
|
40
38
|
/** @type {EventEmitter<"foo">} */
|
|
41
|
-
let ev = new EventEmitter;
|
|
42
|
-
let foo = 0
|
|
39
|
+
let ev = new EventEmitter();
|
|
40
|
+
let foo = 0;
|
|
43
41
|
let action = () => {
|
|
44
42
|
foo++;
|
|
45
43
|
};
|
|
@@ -53,21 +51,20 @@ test("removeListener()", t => {
|
|
|
53
51
|
if (foo == 0) {
|
|
54
52
|
t.pass();
|
|
55
53
|
} else {
|
|
56
|
-
t.fail()
|
|
54
|
+
t.fail();
|
|
57
55
|
}
|
|
58
56
|
});
|
|
59
57
|
|
|
60
|
-
test("Call unsubscriber", t => {
|
|
58
|
+
test("Call unsubscriber", (t) => {
|
|
61
59
|
/** @type {EventEmitter<"foo">} */
|
|
62
|
-
let ev = new EventEmitter;
|
|
63
|
-
let foo = 0
|
|
60
|
+
let ev = new EventEmitter();
|
|
61
|
+
let foo = 0;
|
|
64
62
|
let action = () => {
|
|
65
63
|
foo++;
|
|
66
64
|
};
|
|
67
65
|
|
|
68
66
|
let unsubscriber = ev.on("foo", action);
|
|
69
67
|
|
|
70
|
-
|
|
71
68
|
ev.emit("foo");
|
|
72
69
|
unsubscriber();
|
|
73
70
|
|
|
@@ -76,23 +73,22 @@ test("Call unsubscriber", t => {
|
|
|
76
73
|
if (foo == 1) {
|
|
77
74
|
t.pass();
|
|
78
75
|
} else {
|
|
79
|
-
t.fail()
|
|
76
|
+
t.fail();
|
|
80
77
|
}
|
|
81
78
|
});
|
|
82
79
|
|
|
83
|
-
|
|
84
|
-
test("on(), emit() with error", t => {
|
|
80
|
+
test("on(), emit() with error", (t) => {
|
|
85
81
|
/** @type {EventEmitter<"foo">} */
|
|
86
|
-
let ev = new EventEmitter;
|
|
82
|
+
let ev = new EventEmitter();
|
|
87
83
|
let foo = 0;
|
|
88
84
|
|
|
89
85
|
/**
|
|
90
|
-
*
|
|
91
|
-
* @param {number} bar
|
|
86
|
+
*
|
|
87
|
+
* @param {number} bar
|
|
92
88
|
*/
|
|
93
89
|
function func(bar) {
|
|
94
90
|
if (bar % 2) {
|
|
95
|
-
throw new Error("Custom error")
|
|
91
|
+
throw new Error("Custom error");
|
|
96
92
|
} else {
|
|
97
93
|
foo++;
|
|
98
94
|
}
|
|
@@ -109,15 +105,14 @@ test("on(), emit() with error", t => {
|
|
|
109
105
|
if (foo == 1) {
|
|
110
106
|
t.pass();
|
|
111
107
|
} else {
|
|
112
|
-
t.fail()
|
|
108
|
+
t.fail();
|
|
113
109
|
}
|
|
114
|
-
|
|
115
110
|
});
|
|
116
111
|
|
|
117
|
-
test("waitForEvent()", async t => {
|
|
112
|
+
test("waitForEvent()", async (t) => {
|
|
118
113
|
/** @type {EventEmitter<"foo">} */
|
|
119
|
-
let ev = new EventEmitter;
|
|
120
|
-
let foo = 0
|
|
114
|
+
let ev = new EventEmitter();
|
|
115
|
+
let foo = 0;
|
|
121
116
|
let action = () => {
|
|
122
117
|
foo++;
|
|
123
118
|
};
|
|
@@ -132,14 +127,14 @@ test("waitForEvent()", async t => {
|
|
|
132
127
|
if (foo == 1) {
|
|
133
128
|
t.pass();
|
|
134
129
|
} else {
|
|
135
|
-
t.fail()
|
|
130
|
+
t.fail();
|
|
136
131
|
}
|
|
137
132
|
});
|
|
138
133
|
|
|
139
|
-
test("waitForEvent() with timeout and no event", async t => {
|
|
134
|
+
test("waitForEvent() with timeout and no event", async (t) => {
|
|
140
135
|
/** @type {EventEmitter<"foo">} */
|
|
141
|
-
let ev = new EventEmitter;
|
|
142
|
-
let foo = 0
|
|
136
|
+
let ev = new EventEmitter();
|
|
137
|
+
let foo = 0;
|
|
143
138
|
let action = () => {
|
|
144
139
|
foo++;
|
|
145
140
|
};
|
|
@@ -154,15 +149,14 @@ test("waitForEvent() with timeout and no event", async t => {
|
|
|
154
149
|
if (foo == 0) {
|
|
155
150
|
t.pass();
|
|
156
151
|
} else {
|
|
157
|
-
t.fail()
|
|
152
|
+
t.fail();
|
|
158
153
|
}
|
|
159
154
|
});
|
|
160
155
|
|
|
161
|
-
|
|
162
|
-
test("waitForEvent() with timeout", async t => {
|
|
156
|
+
test("waitForEvent() with timeout", async (t) => {
|
|
163
157
|
/** @type {EventEmitter<"foo">} */
|
|
164
|
-
let ev = new EventEmitter;
|
|
165
|
-
let foo = 0
|
|
158
|
+
let ev = new EventEmitter();
|
|
159
|
+
let foo = 0;
|
|
166
160
|
let action = () => {
|
|
167
161
|
foo++;
|
|
168
162
|
};
|
|
@@ -177,14 +171,13 @@ test("waitForEvent() with timeout", async t => {
|
|
|
177
171
|
if (foo == 1) {
|
|
178
172
|
t.pass();
|
|
179
173
|
} else {
|
|
180
|
-
t.fail()
|
|
174
|
+
t.fail();
|
|
181
175
|
}
|
|
182
176
|
});
|
|
183
177
|
|
|
184
|
-
|
|
185
|
-
test("waitForAnyEvent()", async t => {
|
|
178
|
+
test("waitForAnyEvent()", async (t) => {
|
|
186
179
|
/** @type {EventEmitter<"foo"|"bar">} */
|
|
187
|
-
let ev = new EventEmitter;
|
|
180
|
+
let ev = new EventEmitter();
|
|
188
181
|
let foo = 0;
|
|
189
182
|
let bar = 0;
|
|
190
183
|
|
|
@@ -207,18 +200,17 @@ test("waitForAnyEvent()", async t => {
|
|
|
207
200
|
}, 10);
|
|
208
201
|
|
|
209
202
|
await ev.waitForAnyEvent(["foo", "bar"]);
|
|
210
|
-
|
|
203
|
+
|
|
211
204
|
if (foo == 1 && bar == 1) {
|
|
212
205
|
t.pass();
|
|
213
206
|
} else {
|
|
214
|
-
t.fail()
|
|
207
|
+
t.fail();
|
|
215
208
|
}
|
|
216
209
|
});
|
|
217
210
|
|
|
218
|
-
|
|
219
|
-
test("waitForAnyEvent() with timeout and no event", async t => {
|
|
211
|
+
test("waitForAnyEvent() with timeout and no event", async (t) => {
|
|
220
212
|
/** @type {EventEmitter<"foo"|"bar">} */
|
|
221
|
-
let ev = new EventEmitter;
|
|
213
|
+
let ev = new EventEmitter();
|
|
222
214
|
let foo = 0;
|
|
223
215
|
let bar = 0;
|
|
224
216
|
|
|
@@ -243,13 +235,13 @@ test("waitForAnyEvent() with timeout and no event", async t => {
|
|
|
243
235
|
if (foo == 1 && bar == 0) {
|
|
244
236
|
t.pass();
|
|
245
237
|
} else {
|
|
246
|
-
t.fail()
|
|
238
|
+
t.fail();
|
|
247
239
|
}
|
|
248
240
|
});
|
|
249
241
|
|
|
250
|
-
test("waitForAnyEvent() with timeout", async t => {
|
|
242
|
+
test("waitForAnyEvent() with timeout", async (t) => {
|
|
251
243
|
/** @type {EventEmitter<"foo"|"bar">} */
|
|
252
|
-
let ev = new EventEmitter;
|
|
244
|
+
let ev = new EventEmitter();
|
|
253
245
|
let foo = 0;
|
|
254
246
|
let bar = 0;
|
|
255
247
|
|
|
@@ -266,17 +258,17 @@ test("waitForAnyEvent() with timeout", async t => {
|
|
|
266
258
|
}, 200);
|
|
267
259
|
|
|
268
260
|
await ev.waitForAnyEvent(["foo", "bar"], 50);
|
|
269
|
-
|
|
261
|
+
|
|
270
262
|
if (foo == 0 && bar == 0) {
|
|
271
263
|
t.pass();
|
|
272
264
|
} else {
|
|
273
|
-
t.fail()
|
|
265
|
+
t.fail();
|
|
274
266
|
}
|
|
275
267
|
});
|
|
276
268
|
|
|
277
|
-
test("clearEventListeners()", t => {
|
|
269
|
+
test("clearEventListeners()", (t) => {
|
|
278
270
|
/** @type {EventEmitter<"foo"|"bar">} */
|
|
279
|
-
let ev = new EventEmitter;
|
|
271
|
+
let ev = new EventEmitter();
|
|
280
272
|
let foo = 0;
|
|
281
273
|
|
|
282
274
|
ev.on("foo", () => {
|
|
@@ -293,12 +285,11 @@ test("clearEventListeners()", t => {
|
|
|
293
285
|
ev.emit("foo");
|
|
294
286
|
|
|
295
287
|
t.is(foo, 1);
|
|
296
|
-
}
|
|
297
|
-
);
|
|
288
|
+
});
|
|
298
289
|
|
|
299
|
-
test("destroy()", t => {
|
|
290
|
+
test("destroy()", (t) => {
|
|
300
291
|
/** @type {EventEmitter<"foo">} */
|
|
301
|
-
let ev = new EventEmitter;
|
|
292
|
+
let ev = new EventEmitter();
|
|
302
293
|
let foo = 0;
|
|
303
294
|
|
|
304
295
|
ev.on("foo", () => {
|
|
@@ -315,8 +306,8 @@ test("destroy()", t => {
|
|
|
315
306
|
|
|
316
307
|
ev.destroy();
|
|
317
308
|
|
|
318
|
-
|
|
319
|
-
|
|
309
|
+
t.throws(() => {
|
|
310
|
+
ev.on("foo", () => {});
|
|
320
311
|
});
|
|
321
312
|
|
|
322
313
|
ev.emit("foo");
|
|
@@ -325,4 +316,3 @@ test("destroy()", t => {
|
|
|
325
316
|
|
|
326
317
|
t.is(foo, 0);
|
|
327
318
|
});
|
|
328
|
-
|