Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AsyncEmitter #427

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
73 changes: 73 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,78 @@ Asynchronous some (iterate in series)

Non-blocking synchronous map

### class AsyncEmitter

#### AsyncEmitter.prototype.constructor()

#### AsyncEmitter.prototype.event(name)

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

_Returns:_ { on: [`<Set>`][set], once: [`<Set>`][set] } }

Get or create event

#### 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]|[`<null>`][null]

Add listener

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

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

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

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 event names array

### asyncIter(base)

- `base`: [`<Iterable>`][iterable]|[`<AsyncIterable>`][asynciterable] an
Expand Down Expand Up @@ -921,6 +993,7 @@ Set timeout for asynchronous function execution
[object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
[promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
[set]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
[array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
[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
Expand Down
106 changes: 106 additions & 0 deletions lib/async-emitter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
'use strict';

class AsyncEmitter {
constructor() {
this.events = new Map();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
this.events = new Map();
this.listeners = new Map();

}

// Get or create event
// name <string> event name
// Returns: { on: <Set>, once: <Set> } }
event(name) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to use Symbol('getListeners') for this, to make this property "private".

const { events } = this;
const event = events.get(name);
if (event) return event;
const res = { on: new Set(), once: new Set() };
events.set(name, res);
return res;
}

// Add listener
// name <string> event name
// fn <Function> listener
on(name, fn) {
this.event(name).on.add(fn);
}

// Add listener
// name <string> event name
// fn <Function> listener
// Returns: <Promise> | <null>
once(name, fn) {
if (fn === undefined) {
return new Promise(resolve => {
this.once(name, resolve);
tshemsedinov marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
this.once(name, resolve);
this.once(name, (...args) => resolve(args.length <= 1 ? args[0] : args)
);

});
}
this.event(name).once.add(fn);
return Promise.resolve();
}

// Emit event
// name <string> event name
// args <any[]>
// Returns: <Promise>
emit(name, ...args) {
const { events } = this;
const event = events.get(name);
if (!event) return Promise.resolve();
const { on, once } = event;
const promises = [...on, ...once].map(fn => fn(...args));
once.clear();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to move this one line up, because some of the listeners may create additional once listeners.

if (on.size === 0) events.delete(name);
return Promise.all(promises);
}

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

// Remove all listeners or by name
// name <string> event name
clear(name) {
const { events } = this;
if (!name) events.clear();
else events.delete(name);
}

// Get listeners count by event name
// name <string> event name
// Returns: <number>
count(name) {
const event = this.events.get(name);
if (!event) return 0;
const { on, once } = event;
return on.size + once.size;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: can be simplified to:

return event ? event.on.size + event.once.size : 0;

}

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

// Get event names array
// Returns: <string[]> names
names() {
lundibundi marked this conversation as resolved.
Show resolved Hide resolved
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