Skip to content

Commit

Permalink
Implement AsyncEventEmitter
Browse files Browse the repository at this point in the history
Co-Authored-By: Denys Otrishko <shishugi@gmail.com>
Co-Authored-By: Mykola Bilochub <nbelochub@gmail.com>
Co-Authored-By: Dmytro Nechai <nechaido@gmail.com>
  • Loading branch information
4 people committed Jun 6, 2019
1 parent 29a04ba commit 77f6330
Show file tree
Hide file tree
Showing 5 changed files with 412 additions and 0 deletions.
1 change: 1 addition & 0 deletions .metadocrc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"files": [
"lib/adapters.js",
"lib/array.js",
"lib/async-emitter.js",
"lib/async-iterator.js",
"lib/chain.js",
"lib/collector.js",
Expand Down
65 changes: 65 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,70 @@ Asynchronous some (iterate in series)

Non-blocking synchronous map

### class AsyncEmitter

#### AsyncEmitter.prototype.constructor()

#### AsyncEmitter.prototype.on(name, fn)

- `name`: [`<string>`][string] event name
- `fn`: [`<Function>`][function] listener

Add listener

#### AsyncEmitter.prototype.once(name, fn)

- `name`: [`<string>`][string] event name
- `fn`: [`<Function>`][function] listener

_Returns:_ [`<Promise>`][promise]|[`<undefined>`][undefined]

Add listener

#### async AsyncEmitter.prototype.emit(name, args)

- `name`: [`<string>`][string] event name
- `args`: `<any[]>`

_Returns:_ [`<Promise>`][promise]|[`<undefined>`][undefined]

Emit event

#### AsyncEmitter.prototype.remove(name, fn)

- `name`: [`<string>`][string] event name
- `fn`: [`<Function>`][function] listener to remove

Remove event listener

#### AsyncEmitter.prototype.clear(name)

- `name`: [`<string>`][string] event name

Remove all listeners or by name

#### AsyncEmitter.prototype.count(name)

- `name`: [`<string>`][string] event name

_Returns:_ [`<number>`][number]

Get listeners count by event name

#### AsyncEmitter.prototype.listeners(name)

- `name`: [`<string>`][string] event name

_Returns:_ [`<Function[]>`][function]

Get listeners array by event name

#### AsyncEmitter.prototype.names()

_Returns:_ [`<string[]>`][string] names

Get events name array

### asyncIter(base)

- `base`: [`<Iterable>`][iterable]|[`<AsyncIterable>`][asynciterable] an
Expand Down Expand Up @@ -925,6 +989,7 @@ Set timeout for asynchronous function execution
[error]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type
[null]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Null_type
[undefined]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Undefined_type
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type
[iterable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
Expand Down
110 changes: 110 additions & 0 deletions lib/async-emitter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
'use strict';

const { iter } = require('@metarhia/common');

class AsyncEmitter {
constructor() {
this.events = new Map();
this.wrappers = new Map();
}

// Add listener
// name <string> event name
// fn <Function> listener
on(name, fn) {
let event = this.events.get(name);
if (!event) {
event = new Set();
this.events.set(name, event);
}
event.add(fn);
}

// Add listener
// name <string> event name
// fn <Function> listener
// Returns: <Promise> | <undefined>
once(name, fn) {
if (fn === undefined) {
return new Promise(resolve => {
this.once(name, resolve);
});
}
const wrapper = (...args) => {
this.remove(name, fn);
return fn(...args);
};
this.wrappers.set(fn, wrapper);
this.on(name, wrapper);
return undefined;
}

// Emit event
// name <string> event name
// args <any[]>
// Returns: <Promise> | <undefined>
async emit(name, ...args) {
const event = this.events.get(name);
if (!event) return undefined;
const listeners = event.values();
const promises = iter(listeners).map(fn => fn(...args));
return Promise.all(promises);
}

// Remove event listener
// name <string> event name
// fn <Function> listener to remove
remove(name, fn) {
const { events, wrappers } = this;
const event = events.get(name);
if (!event) return;
if (event.has(fn)) event.delete(fn);
const wrapper = wrappers.get(fn);
if (wrapper) {
wrappers.delete(fn);
event.delete(wrapper);
}
if (event.size === 0) events.delete(name);
}

// Remove all listeners or by name
// name <string> event name
clear(name) {
const { events, wrappers } = this;
if (!name) {
events.clear();
wrappers.clear();
return;
}
const event = events.get(name);
if (!event) return;
for (const [fn, wrapper] of wrappers.entries()) {
if (event.has(wrapper)) wrappers.delete(fn);
}
events.delete(name);
}

// Get listeners count by event name
// name <string> event name
// Returns: <number>
count(name) {
const event = this.events.get(name);
return event ? event.size : 0;
}

// Get listeners array by event name
// name <string> event name
// Returns: <Function[]>
listeners(name) {
const event = this.events.get(name);
return [...event];
}

// Get event names array
// Returns: <string[]> names
names() {
return [...this.events.keys()];
}
}

module.exports = { AsyncEmitter };
1 change: 1 addition & 0 deletions metasync.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const submodules = [
'composition', // Unified abstraction
'adapters', // Adapters to convert different async contracts
'array', // Array utilities
'async-emitter', // AsyncEmitter
'chain', // Process arrays sync and async array in chain
'collector', // DataCollector and KeyCollector
'control', // Control flow utilities
Expand Down

0 comments on commit 77f6330

Please sign in to comment.