Skip to content

Commit c67eb7b

Browse files
tshemsedinovlundibundibelochubnechaido
committed
Implement AsyncEventEmitter
Co-Authored-By: Denys Otrishko <shishugi@gmail.com> Co-Authored-By: Mykola Bilochub <nbelochub@gmail.com> Co-Authored-By: Dmytro Nechai <nechaido@gmail.com>
1 parent 29a04ba commit c67eb7b

File tree

5 files changed

+442
-0
lines changed

5 files changed

+442
-0
lines changed

.metadocrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"files": [
77
"lib/adapters.js",
88
"lib/array.js",
9+
"lib/async-emitter.js",
910
"lib/async-iterator.js",
1011
"lib/chain.js",
1112
"lib/collector.js",

README.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,70 @@ Asynchronous some (iterate in series)
301301

302302
Non-blocking synchronous map
303303

304+
### class AsyncEmitter
305+
306+
#### AsyncEmitter.prototype.constructor()
307+
308+
#### AsyncEmitter.prototype.on(name, fn)
309+
310+
- `name`: [`<string>`][string] event name
311+
- `fn`: [`<Function>`][function] listener
312+
313+
Add listener
314+
315+
#### AsyncEmitter.prototype.once(name, fn)
316+
317+
- `name`: [`<string>`][string] event name
318+
- `fn`: [`<Function>`][function] listener
319+
320+
_Returns:_ [`<Promise>`][promise]|[`<undefined>`][undefined]
321+
322+
Add listener
323+
324+
#### async AsyncEmitter.prototype.emit(name, args)
325+
326+
- `name`: [`<string>`][string] event name
327+
- `args`: `<any[]>`
328+
329+
_Returns:_ [`<Promise>`][promise]|[`<undefined>`][undefined]
330+
331+
Emit event
332+
333+
#### AsyncEmitter.prototype.remove(name, fn)
334+
335+
- `name`: [`<string>`][string] event name
336+
- `fn`: [`<Function>`][function] listener to remove
337+
338+
Remove event listener
339+
340+
#### AsyncEmitter.prototype.clear(name)
341+
342+
- `name`: [`<string>`][string] event name
343+
344+
Remove all listeners or by name
345+
346+
#### AsyncEmitter.prototype.count(name)
347+
348+
- `name`: [`<string>`][string] event name
349+
350+
_Returns:_ [`<number>`][number]
351+
352+
Get listeners count by event name
353+
354+
#### AsyncEmitter.prototype.listeners(name)
355+
356+
- `name`: [`<string>`][string] event name
357+
358+
_Returns:_ [`<Function[]>`][function]
359+
360+
Get listeners array by event name
361+
362+
#### AsyncEmitter.prototype.names()
363+
364+
_Returns:_ [`<string[]>`][string] names
365+
366+
Get events name array
367+
304368
### asyncIter(base)
305369

306370
- `base`: [`<Iterable>`][iterable]|[`<AsyncIterable>`][asynciterable] an
@@ -925,6 +989,7 @@ Set timeout for asynchronous function execution
925989
[error]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
926990
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type
927991
[null]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Null_type
992+
[undefined]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Undefined_type
928993
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type
929994
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type
930995
[iterable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols

lib/async-emitter.js

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
'use strict';
2+
3+
class AsyncEmitter {
4+
constructor() {
5+
this.events = new Map();
6+
}
7+
8+
// Get or create event
9+
// name <string> event name
10+
// Returns: { on <Function>, once <Function> }
11+
event(name) {
12+
let event = this.events.get(name);
13+
if (event) return event;
14+
const on = new Set();
15+
const once = new Map();
16+
event = { on, once };
17+
this.events.set(name, event);
18+
return event;
19+
}
20+
21+
// Add listener
22+
// name <string> event name
23+
// fn <Function> listener
24+
on(name, fn) {
25+
const event = this.event(name);
26+
event.on.add(fn);
27+
}
28+
29+
// Add listener
30+
// name <string> event name
31+
// fn <Function> listener
32+
// Returns: <Promise> | <undefined>
33+
once(name, fn) {
34+
if (fn === undefined) {
35+
return new Promise(resolve => {
36+
this.once(name, resolve);
37+
});
38+
}
39+
const wrapper = (...args) => {
40+
this.remove(name, fn, true);
41+
return fn(...args);
42+
};
43+
const { once } = this.event(name);
44+
once.set(fn, wrapper);
45+
return undefined;
46+
}
47+
48+
// Emit event
49+
// name <string> event name
50+
// args <any[]>
51+
// Returns: <Promise> | <undefined>
52+
async emit(name, ...args) {
53+
const event = this.events.get(name);
54+
if (!event) return undefined;
55+
const { on, once } = event;
56+
const listeners = [...on.values(), ...once.values()];
57+
const promises = listeners.map(fn => fn(...args));
58+
return Promise.all(promises);
59+
}
60+
61+
// Remove event listener
62+
// name <string> event name
63+
// fn <Function> listener to remove
64+
// onceOnly <boolean> remove once only
65+
remove(name, fn, onceOnly = false) {
66+
const { events } = this;
67+
const event = events.get(name);
68+
if (!event) return;
69+
const { on, once } = event;
70+
if (!onceOnly) on.delete(fn);
71+
once.delete(fn);
72+
if (on.size === 0 && once.size === 0) events.delete(name);
73+
}
74+
75+
// Remove all listeners or by name
76+
// name <string> event name
77+
clear(name) {
78+
const { events } = this;
79+
if (!name) {
80+
events.clear();
81+
return;
82+
}
83+
const event = events.get(name);
84+
if (event) events.delete(name);
85+
}
86+
87+
// Get listeners count by event name
88+
// name <string> event name
89+
// Returns: <number>
90+
count(name) {
91+
const event = this.events.get(name);
92+
if (!event) return 0;
93+
const { on, once } = event;
94+
return on.size + once.size;
95+
}
96+
97+
// Get listeners array by event name
98+
// name <string> event name
99+
// Returns: <Function[]>
100+
listeners(name) {
101+
const event = this.events.get(name);
102+
if (!event) return [];
103+
const { on, once } = event;
104+
return [...on.values(), ...once.keys()];
105+
}
106+
107+
// Get event names array
108+
// Returns: <string[]> names
109+
names() {
110+
return [...this.events.keys()];
111+
}
112+
}
113+
114+
module.exports = { AsyncEmitter };

metasync.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const submodules = [
77
'composition', // Unified abstraction
88
'adapters', // Adapters to convert different async contracts
99
'array', // Array utilities
10+
'async-emitter', // AsyncEmitter
1011
'chain', // Process arrays sync and async array in chain
1112
'collector', // DataCollector and KeyCollector
1213
'control', // Control flow utilities

0 commit comments

Comments
 (0)