Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

emittery

Package Overview
Dependencies
Maintainers
2
Versions
30
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

emittery - npm Package Compare versions

Comparing version 0.4.1 to 0.5.0

index.d.ts

207

index.js

@@ -5,2 +5,4 @@ 'use strict';

const eventsMap = new WeakMap();
const producersMap = new WeakMap();
const anyProducer = Symbol('anyProducer');
const resolvedPromise = Promise.resolve();

@@ -29,6 +31,155 @@

function getEventProducers(instance, eventName) {
const key = typeof eventName === 'string' ? eventName : anyProducer;
const producers = producersMap.get(instance);
if (!producers.has(key)) {
producers.set(key, new Set());
}
return producers.get(key);
}
function enqueueProducers(instance, eventName, eventData) {
const producers = producersMap.get(instance);
if (producers.has(eventName)) {
for (const producer of producers.get(eventName)) {
producer.enqueue(eventData);
}
}
if (producers.has(anyProducer)) {
const item = Promise.all([eventName, eventData]);
for (const producer of producers.get(anyProducer)) {
producer.enqueue(item);
}
}
}
function iterator(instance, eventName) {
let isFinished = false;
let flush = () => {};
let queue = [];
const producer = {
enqueue(item) {
queue.push(item);
flush();
},
finish() {
isFinished = true;
flush();
}
};
getEventProducers(instance, eventName).add(producer);
return {
async next() {
if (!queue) {
return {done: true};
}
if (queue.length === 0) {
if (isFinished) {
queue = undefined;
return this.next();
}
await new Promise(resolve => {
flush = resolve;
});
return this.next();
}
return {
done: false,
value: await queue.shift()
};
},
async return(value) {
queue = undefined;
getEventProducers(instance, eventName).delete(producer);
flush();
return arguments.length > 0 ?
{done: true, value: await value} :
{done: true};
},
[Symbol.asyncIterator]() {
return this;
}
};
}
function defaultMethodNamesOrAssert(methodNames) {
if (methodNames === undefined) {
return allEmitteryMethods;
}
if (!Array.isArray(methodNames)) {
throw new TypeError('`methodNames` must be an array of strings');
}
for (const methodName of methodNames) {
if (!allEmitteryMethods.includes(methodName)) {
if (typeof methodName !== 'string') {
throw new TypeError('`methodNames` element must be a string');
}
throw new Error(`${methodName} is not Emittery method`);
}
}
return methodNames;
}
class Emittery {
static mixin(emitteryPropertyName, methodNames) {
methodNames = defaultMethodNamesOrAssert(methodNames);
return target => {
if (typeof target !== 'function') {
throw new TypeError('`target` must be function');
}
for (const methodName of methodNames) {
if (target.prototype[methodName] !== undefined) {
throw new Error(`The property \`${methodName}\` already exists on \`target\``);
}
}
function getEmitteryProperty() {
Object.defineProperty(this, emitteryPropertyName, {
enumerable: false,
value: new Emittery()
});
return this[emitteryPropertyName];
}
Object.defineProperty(target.prototype, emitteryPropertyName, {
enumerable: false,
get: getEmitteryProperty
});
const emitteryMethodCaller = methodName => function (...args) {
return this[emitteryPropertyName][methodName](...args);
};
for (const methodName of methodNames) {
Object.defineProperty(target.prototype, methodName, {
enumerable: false,
value: emitteryMethodCaller(methodName)
});
}
return target;
};
}
constructor() {
anyMap.set(this, new Set());
eventsMap.set(this, new Map());
producersMap.set(this, new Map());
}

@@ -59,5 +210,12 @@

events(eventName) {
assertEventName(eventName);
return iterator(this, eventName);
}
async emit(eventName, eventData) {
assertEventName(eventName);
enqueueProducers(this, eventName, eventData);
const listeners = getListeners(this, eventName);

@@ -113,2 +271,6 @@ const anyListeners = anyMap.get(this);

anyEvent() {
return iterator(this);
}
offAny(listener) {

@@ -122,7 +284,24 @@ assertListener(listener);

getListeners(this, eventName).clear();
const producers = getEventProducers(this, eventName);
for (const producer of producers) {
producer.finish();
}
producers.clear();
} else {
anyMap.get(this).clear();
for (const listeners of eventsMap.get(this).values()) {
listeners.clear();
}
for (const producers of producersMap.get(this).values()) {
for (const producer of producers) {
producer.finish();
}
producers.clear();
}
}

@@ -133,3 +312,4 @@ }

if (typeof eventName === 'string') {
return anyMap.get(this).size + getListeners(this, eventName).size;
return anyMap.get(this).size + getListeners(this, eventName).size +
getEventProducers(this, eventName).size + getEventProducers(this).size;
}

@@ -147,6 +327,31 @@

for (const value of producersMap.get(this).values()) {
count += value.size;
}
return count;
}
bindMethods(target, methodNames) {
if (typeof target !== 'object' || target === null) {
throw new TypeError('`target` must be an object');
}
methodNames = defaultMethodNamesOrAssert(methodNames);
for (const methodName of methodNames) {
if (target[methodName] !== undefined) {
throw new Error(`The property \`${methodName}\` already exists on \`target\``);
}
Object.defineProperty(target, methodName, {
enumerable: false,
value: this[methodName].bind(this)
});
}
}
}
const allEmitteryMethods = Object.getOwnPropertyNames(Emittery.prototype).filter(v => v !== 'constructor');
// Subclass used to encourage TS users to type their events.

@@ -153,0 +358,0 @@ Emittery.Typed = class extends Emittery {};

38

package.json
{
"name": "emittery",
"version": "0.4.1",
"version": "0.5.0",
"description": "Simple and modern async event emitter",

@@ -13,17 +13,11 @@ "license": "MIT",

"engines": {
"node": ">=6"
"node": ">=8"
},
"scripts": {
"build": "babel --out-file=legacy.js index.js",
"build:watch": "npm run build -- --watch",
"prepare": "npm run build",
"test": "xo && tsc --noEmit && nyc ava"
"test": "xo && nyc ava && tsd"
},
"files": [
"Emittery.d.ts",
"index.js",
"legacy.js",
"legacy.d.ts"
"index.d.ts"
],
"typings": "./Emittery.d.ts",
"keywords": [

@@ -51,17 +45,15 @@ "event",

"await",
"promise"
"promise",
"typescript",
"ts",
"typed"
],
"devDependencies": {
"@types/node": "^10.1.2",
"ava": "*",
"babel-cli": "^6.26.0",
"babel-core": "^6.26.0",
"babel-plugin-transform-async-to-generator": "^6.24.1",
"codecov": "^3.0.0",
"delay": "^3.0.0",
"glob": "^7.1.2",
"nyc": "^12.0.2",
"ts-node": "^6.0.3",
"typescript": "^2.9.0",
"xo": "*"
"@types/node": "^12.7.5",
"ava": "^2.4.0",
"codecov": "^3.1.0",
"delay": "^4.3.0",
"nyc": "^14.1.1",
"tsd": "^0.7.4",
"xo": "^0.24.0"
},

@@ -68,0 +60,0 @@ "nyc": {

@@ -9,3 +9,3 @@ # <img src="media/header.png" width="1000">

It's works in Node.js and the browser (using a bundler).
It works in Node.js and the browser (using a bundler).

@@ -26,2 +26,3 @@ Emitting events asynchronously is important for production code where you want the least amount of synchronous operations.

const Emittery = require('emittery');
const emitter = new Emittery();

@@ -37,7 +38,3 @@

### Node.js 6
The above only works in Node.js 8 or newer. For older Node.js versions you can use `require('emittery/legacy')`.
## API

@@ -70,2 +67,6 @@

```js
const Emittery = require('emittery');
const emitter = new Emittery();
emitter.once('🦄').then(data => {

@@ -79,10 +80,62 @@ console.log(data);

#### emit(eventName, [data])
#### events(eventName)
Trigger an event asynchronously, optionally with some data. Listeners are called in the order they were added, but execute concurrently.
Get an async iterator which buffers data each time an event is emitted.
Returns a promise for when all the event listeners are done. *Done* meaning executed if synchronous or resolved when an async/promise-returning function. You usually wouldn't want to wait for this, but you could for example catch possible errors. If any of the listeners throw/reject, the returned promise will be rejected with the error, but the other listeners will not be affected.
Call `return()` on the iterator to remove the subscription.
#### emitSerial(eventName, [data])
```js
const Emittery = require('emittery');
const emitter = new Emittery();
const iterator = emitter.events('🦄');
emitter.emit('🦄', '🌈1'); // Buffered
emitter.emit('🦄', '🌈2'); // Buffered
iterator
.next()
.then(({value, done}) => {
// done === false
// value === '🌈1'
return iterator.next();
})
.then(({value, done}) => {
// done === false
// value === '🌈2'
// Revoke subscription
return iterator.return();
})
.then(({done}) => {
// done === true
});
```
In practice, you would usually consume the events using the [for await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of) statement. In that case, to revoke the subscription simply break the loop.
```js
const Emittery = require('emittery');
const emitter = new Emittery();
const iterator = emitter.events('🦄');
emitter.emit('🦄', '🌈1'); // Buffered
emitter.emit('🦄', '🌈2'); // Buffered
// In an async context.
for await (const data of iterator) {
if (data === '🌈2') {
break; // Revoke the subscription when we see the value '🌈2'.
}
}
```
#### emit(eventName, data?)
Trigger an event asynchronously, optionally with some data. Listeners are called in the order they were added, but executed concurrently.
Returns a promise that resolves when all the event listeners are done. *Done* meaning executed if synchronous or resolved when an async/promise-returning function. You usually wouldn't want to wait for this, but you could for example catch possible errors. If any of the listeners throw/reject, the returned promise will be rejected with the error, but the other listeners will not be affected.
#### emitSerial(eventName, data?)
Same as above, but it waits for each listener to resolve before triggering the next one. This can be useful if your events depend on each other. Although ideally they should not. Prefer `emit()` whenever possible.

@@ -104,2 +157,36 @@

#### anyEvent()
Get an async iterator which buffers a tuple of an event name and data each time an event is emitted.
Call `return()` on the iterator to remove the subscription.
```js
const Emittery = require('emittery');
const emitter = new Emittery();
const iterator = emitter.anyEvent();
emitter.emit('🦄', '🌈1'); // Buffered
emitter.emit('🌟', '🌈2'); // Buffered
iterator.next()
.then(({value, done}) => {
// done === false
// value is ['🦄', '🌈1']
return iterator.next();
})
.then(({value, done}) => {
// done === false
// value is ['🌟', '🌈2']
// Revoke subscription
return iterator.return();
})
.then(({done}) => {
// done === true
});
```
In the same way as for `events`, you can subscribe by using the `for await` statement
#### clearListeners()

@@ -111,21 +198,49 @@

#### listenerCount([eventName])
#### listenerCount(eventName?)
The number of listeners for the `eventName` or all events if not specified.
#### bindMethods(target, methodNames?)
Bind the given `methodNames`, or all `Emittery` methods if `methodNames` is not defined, into the `target` object.
```js
import Emittery = require('emittery');
const object = {};
new Emittery().bindMethods(object);
object.emit('event');
```
## TypeScript
Definition for `emittery` and `emittery/legacy` are included. Use `import Emittery = require('emittery')` or `import Emittery = require('emittery/legacy')` to load the desired implementation.
The default `Emittery` class does not let you type allowed event names and their associated data. However, you can use `Emittery.Typed` with generics:
The default `Emittery` class does not let you type allowed event names and their associated data. However you can use `Emittery.Typed` with generics:
```ts
import Emittery = require('emittery');
const emitter = new Emittery.Typed<{value: string}, 'open' | 'close'>();
emitter.emit('open');
emitter.emit('value', 'foo\n');
emitter.emit('value', 1); // TS compilation error
emitter.emit('end'); // TS compilation error
```
### Emittery.mixin(emitteryPropertyName, methodNames?)
A decorator which mixins `Emittery` as property `emitteryPropertyName` and `methodNames`, or all `Emittery` methods if `methodNames` is not defined, into the target class.
```ts
import Emittery = require('emittery');
const ee = new Emittery.Typed<{value: string}, 'open' | 'close'>();
@Emittery.mixin('emittery')
class MyClass {}
ee.emit('open');
ee.emit('value', 'foo\n');
ee.emit('value', 1); // TS compilation error
ee.emit('end'); // TS compilation error
const instance = new MyClass();
instance.emit('event');
```

@@ -184,6 +299,1 @@

- [p-event](https://github.com/sindresorhus/p-event) - Promisify an event by waiting for it to be emitted
## License
MIT © [Sindre Sorhus](https://sindresorhus.com)
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc