eventemitter2
Advanced tools
Comparing version 6.2.1 to 6.3.0
@@ -9,2 +9,8 @@ # Change Log | ||
## [6.3.0] - 2020-03-28 | ||
### Added | ||
- emitter.getMaxListeners() & EventEmitter2.defaultMaxListeners() @DigitalBrainJS | ||
- EventEmitter2.once for feature pairity with EventEmitter.once @DigitalBrainJS | ||
## [6.2.1] - 2020-03-20 | ||
@@ -11,0 +17,0 @@ |
@@ -46,2 +46,4 @@ export type eventNS = string[]; | ||
interface WaitForFilter { (...values: any[]): boolean } | ||
export interface WaitForOptions { | ||
@@ -55,13 +57,36 @@ /** | ||
*/ | ||
filter: { (...values: any[]): boolean }, | ||
filter: WaitForFilter, | ||
/** | ||
* @default false | ||
*/ | ||
handleError: boolean | ||
handleError: boolean, | ||
/** | ||
* @default Promise | ||
*/ | ||
Promise: Function, | ||
/** | ||
* @default false | ||
*/ | ||
overload: boolean | ||
} | ||
export interface WaitForThenable<T> extends Promise<T>{ | ||
export interface CancelablePromise<T> extends Promise<T>{ | ||
cancel(reason: string): undefined | ||
} | ||
export interface OnceOptions { | ||
/** | ||
* @default 0 | ||
*/ | ||
timeout: number, | ||
/** | ||
* @default Promise | ||
*/ | ||
Promise: Function, | ||
/** | ||
* @default false | ||
*/ | ||
overload: boolean | ||
} | ||
export declare class EventEmitter2 { | ||
@@ -85,6 +110,11 @@ constructor(options?: ConstructorOptions) | ||
setMaxListeners(n: number): void; | ||
getMaxListeners(): number; | ||
eventNames(): string[]; | ||
listeners(event: string | string[]): Listener[] // TODO: not in documentation by Willian | ||
listeners(event: string | string[]): Listener[] | ||
listenersAny(): Listener[] // TODO: not in documentation by Willian | ||
waitFor(event: string, options?: WaitForOptions): WaitForThenable<any> | ||
waitFor(event: string, timeout?: number): CancelablePromise<any[]> | ||
waitFor(event: string, filter?: WaitForFilter): CancelablePromise<any[]> | ||
waitFor(event: string, options?: WaitForOptions): CancelablePromise<any[]> | ||
static once(emitter: EventEmitter2, event: string | symbol, options?: OnceOptions): CancelablePromise<any[]> | ||
static defaultMaxListeners: number; | ||
} |
@@ -9,3 +9,3 @@ /*! | ||
;!function(undefined) { | ||
var hasOwnProperty= Object.hasOwnProperty; | ||
var isArray = Array.isArray ? Array.isArray : function _isArray(obj) { | ||
@@ -28,4 +28,7 @@ return Object.prototype.toString.call(obj) === "[object Array]"; | ||
conf.delimiter && (this.delimiter = conf.delimiter); | ||
this._maxListeners = conf.maxListeners !== undefined ? conf.maxListeners : defaultMaxListeners; | ||
if(conf.maxListeners!==undefined){ | ||
this._maxListeners= conf.maxListeners; | ||
} | ||
conf.wildcard && (this.wildcard = conf.wildcard); | ||
@@ -40,4 +43,2 @@ conf.newListener && (this._newListener = conf.newListener); | ||
} | ||
} else { | ||
this._maxListeners = defaultMaxListeners; | ||
} | ||
@@ -90,2 +91,135 @@ } | ||
function resolveOptions(options, schema, reducers, allowUnknown) { | ||
var computedOptions = Object.assign({}, schema); | ||
if(!options) return computedOptions; | ||
if (typeof options !== 'object') { | ||
throw TypeError('options must be an object') | ||
} | ||
var keys = Object.keys(options); | ||
var length = keys.length; | ||
var option, value; | ||
var reducer; | ||
function reject(reason){ | ||
throw Error('Invalid "' + option + '" option value' + (reason? '. Reason: '+ reason : '')) | ||
} | ||
for (var i = 0; i < length; i++) { | ||
option = keys[i]; | ||
if (!allowUnknown && !hasOwnProperty.call(schema, option)) { | ||
throw Error('Unknown "' + option + '" option'); | ||
} | ||
value = options[option]; | ||
if (value !== undefined) { | ||
reducer = reducers[option]; | ||
computedOptions[option] = reducer ? reducer(value, reject) : value; | ||
} | ||
} | ||
return computedOptions; | ||
} | ||
function constructorReducer(value, reject) { | ||
if (typeof value !== 'function' || !value.hasOwnProperty('prototype')) { | ||
reject('Promise option must be a constructor'); | ||
} | ||
return value; | ||
} | ||
function functionReducer(value, reject) { | ||
if (typeof value !== 'function') { | ||
reject('Promise option must be a function'); | ||
} | ||
return value; | ||
} | ||
function makeCancelablePromise(Promise, executor, options) { | ||
var isCancelable; | ||
var callbacks; | ||
var timer= 0; | ||
var subscribeClosed; | ||
var promise = new Promise(function (resolve, reject, onCancel) { | ||
options= resolveOptions(options, { | ||
timeout: 0, | ||
overload: false | ||
}, { | ||
timeout: function(value, reject){ | ||
value*= 1; | ||
if (typeof value !== 'number' || value < 0 || !Number.isFinite(value)) { | ||
reject('timeout must be a positive number'); | ||
} | ||
return value; | ||
} | ||
}); | ||
isCancelable = !options.overload && typeof Promise.prototype.cancel === 'function' && typeof onCancel === 'function'; | ||
function cleanup() { | ||
if (callbacks) { | ||
callbacks = null; | ||
} | ||
if (timer) { | ||
clearTimeout(timer); | ||
timer = 0; | ||
} | ||
} | ||
var _resolve= function(value){ | ||
cleanup(); | ||
resolve(value); | ||
}; | ||
var _reject= function(err){ | ||
cleanup(); | ||
reject(err); | ||
}; | ||
if (isCancelable) { | ||
executor(_resolve, _reject, onCancel); | ||
} else { | ||
callbacks = [function(reason){ | ||
_reject(reason || Error('canceled')); | ||
}]; | ||
executor(_resolve, _reject, function (cb) { | ||
if (subscribeClosed) { | ||
throw Error('Unable to subscribe on cancel event asynchronously') | ||
} | ||
if (typeof cb !== 'function') { | ||
throw TypeError('onCancel callback must be a function'); | ||
} | ||
callbacks.push(cb); | ||
}); | ||
subscribeClosed= true; | ||
} | ||
if (options.timeout > 0) { | ||
timer= setTimeout(function(){ | ||
var reason= Error('timeout'); | ||
timer= 0; | ||
promise.cancel(reason); | ||
reject(reason); | ||
}, options.timeout); | ||
} | ||
}); | ||
if (!isCancelable) { | ||
promise.cancel = function (reason) { | ||
if (!callbacks) { | ||
return; | ||
} | ||
var length = callbacks.length; | ||
for (var i = 1; i < length; i++) { | ||
callbacks[i](reason); | ||
} | ||
// internal callback to reject the promise | ||
callbacks[0](reason); | ||
callbacks = null; | ||
}; | ||
} | ||
return promise; | ||
} | ||
function EventEmitter(conf) { | ||
@@ -280,2 +414,6 @@ this._events = {}; | ||
EventEmitter.prototype.getMaxListeners = function() { | ||
return this._maxListeners; | ||
}; | ||
EventEmitter.prototype.event = ''; | ||
@@ -298,7 +436,7 @@ | ||
return this._many(event, ttl, fn, false); | ||
} | ||
}; | ||
EventEmitter.prototype.prependMany = function(event, ttl, fn) { | ||
return this._many(event, ttl, fn, true); | ||
} | ||
}; | ||
@@ -569,3 +707,3 @@ EventEmitter.prototype._many = function(event, ttl, fn, prepend) { | ||
return this; | ||
} | ||
}; | ||
@@ -622,3 +760,3 @@ EventEmitter.prototype._on = function(type, listener, prepend) { | ||
return this; | ||
} | ||
}; | ||
@@ -785,3 +923,3 @@ EventEmitter.prototype.off = function(type, listener) { | ||
return Object.keys(this._events); | ||
} | ||
}; | ||
@@ -805,57 +943,157 @@ EventEmitter.prototype.listenerCount = function(type) { | ||
var self = this; | ||
var handleError = options && options.handleError !== undefined ? options.handleError : false; | ||
var filter = options && options.filter !== undefined ? options.filter : false; | ||
var timeout = options && options.timeout !== undefined ? options.timeout : 0; | ||
var cancel; | ||
var type = typeof options; | ||
if (type === 'number') { | ||
options = {timeout: options}; | ||
} else if (type === 'function') { | ||
options = {filter: options}; | ||
} | ||
var promise = new Promise(function (resolve, reject) { | ||
var detached; | ||
var isDone; | ||
var timer = timeout > 0 && setTimeout(function () { | ||
timer = 0; | ||
done(new Error('timeout')) | ||
}, timeout); | ||
options= resolveOptions(options, { | ||
timeout: 0, | ||
filter: undefined, | ||
handleError: false, | ||
Promise: Promise, | ||
overload: false | ||
}, { | ||
filter: functionReducer, | ||
Promise: constructorReducer | ||
}); | ||
function done(err, data) { | ||
if (isDone) return; | ||
isDone = true; | ||
!detached && self.off(event, listener); | ||
detached = true; | ||
timer && clearTimeout(timer); | ||
timer = 0; | ||
err ? reject(err) : resolve(data); | ||
} | ||
return makeCancelablePromise(options.Promise, function (resolve, reject, onCancel) { | ||
function listener() { | ||
var filter= options.filter; | ||
if (filter && !filter.apply(self, arguments)) { | ||
return; | ||
} | ||
if (handleError) { | ||
if (options.handleError) { | ||
var err = arguments[0]; | ||
err ? done(err) : done(null, toArray.apply(null, arguments).slice(1)); | ||
err ? reject(err) : resolve(toArray.apply(null, arguments).slice(1)); | ||
} else { | ||
done(null, toArray.apply(null, arguments)); | ||
resolve(toArray.apply(null, arguments)); | ||
} | ||
} | ||
cancel = function (reason) { | ||
done(new Error(reason || 'canceled')) | ||
}; | ||
onCancel(function(){ | ||
self.off(event, listener); | ||
}); | ||
self._on(event, listener, false); | ||
}, { | ||
timeout: options.timeout, | ||
overload: options.overload | ||
}) | ||
}; | ||
function once(emitter, name, options) { | ||
options= resolveOptions(options, { | ||
Promise: Promise, | ||
timeout: 0, | ||
overload: false | ||
}, { | ||
Promise: constructorReducer | ||
}); | ||
return Object.create(self, { | ||
then: { | ||
value: function (resolve, reject) { | ||
return promise.then(resolve, reject); | ||
var _Promise= options.Promise; | ||
return makeCancelablePromise(_Promise, function(resolve, reject, onCancel){ | ||
var handler; | ||
if (typeof emitter.addEventListener === 'function') { | ||
handler= function () { | ||
resolve(toArray.apply(null, arguments)); | ||
}; | ||
onCancel(function(){ | ||
emitter.removeEventListener(name, handler); | ||
}); | ||
emitter.addEventListener( | ||
name, | ||
handler, | ||
{once: true} | ||
); | ||
return; | ||
} | ||
var eventListener = function(){ | ||
errorListener && emitter.removeListener('error', errorListener); | ||
resolve(toArray.apply(null, arguments)); | ||
}; | ||
var errorListener; | ||
if (name !== 'error') { | ||
errorListener = function (err){ | ||
emitter.removeListener(name, eventListener); | ||
reject(err); | ||
}; | ||
emitter.once('error', errorListener); | ||
} | ||
onCancel(function(){ | ||
errorListener && emitter.removeListener('error', errorListener); | ||
emitter.removeListener(name, eventListener); | ||
}); | ||
emitter.once(name, eventListener); | ||
}, { | ||
timeout: options.timeout, | ||
overload: options.overload | ||
}); | ||
} | ||
var prototype= EventEmitter.prototype; | ||
Object.defineProperties(EventEmitter, { | ||
defaultMaxListeners: { | ||
get: function () { | ||
return prototype._maxListeners; | ||
}, | ||
set: function (n) { | ||
if (typeof n !== 'number' || n < 0 || Number.isNaN(n)) { | ||
throw TypeError('n must be a non-negative number') | ||
} | ||
prototype._maxListeners = n; | ||
}, | ||
cancel: { | ||
value: function () { | ||
cancel && cancel(); | ||
enumerable: true | ||
}, | ||
once: { | ||
value: once, | ||
writable: true, | ||
configurable: true | ||
} | ||
}); | ||
Object.defineProperties(prototype, { | ||
_maxListeners: { | ||
value: defaultMaxListeners, | ||
writable: true, | ||
configurable: true | ||
} | ||
}); | ||
var prototype= EventEmitter.prototype; | ||
Object.defineProperties(EventEmitter, { | ||
defaultMaxListeners: { | ||
get: function(){ | ||
return prototype._maxListeners; | ||
}, | ||
set: function (n) { | ||
if (typeof n !== 'number' || n < 0 || Number.isNaN(n)) { | ||
throw TypeError('n must be a non-negative number') | ||
} | ||
prototype._maxListeners = n; | ||
}, | ||
enumerable: true | ||
} | ||
}); | ||
Object.defineProperties(prototype, { | ||
_maxListeners: { | ||
value: defaultMaxListeners, | ||
writable: true, | ||
configurable: true | ||
} | ||
}); | ||
}; | ||
}); | ||
@@ -862,0 +1100,0 @@ if (typeof define === 'function' && define.amd) { |
{ | ||
"name": "eventemitter2", | ||
"version": "6.2.1", | ||
"version": "6.3.0", | ||
"description": "A Node.js event emitter implementation with namespaces, wildcards, TTL and browser support.", | ||
@@ -23,2 +23,3 @@ "keywords": [ | ||
"benchmark": ">= 0.2.2", | ||
"bluebird": "^3.7.2", | ||
"nodeunit": "*", | ||
@@ -25,0 +26,0 @@ "nyc": "^11.4.1" |
109
README.md
@@ -13,5 +13,8 @@ [![Codeship](https://img.shields.io/codeship/3ad58940-4c7d-0131-15d5-5a8cd3f550f8.svg?maxAge=2592000)]() | ||
### FEATURES | ||
- Namespaces/Wildcards. | ||
- Times To Listen (TTL), extends the `once` concept with `many`. | ||
- Browser environment compatibility. | ||
- Namespaces/Wildcards | ||
- Times To Listen (TTL), extends the `once` concept with [`many`](#emittermanyevent-timestolisten-listener) | ||
- The [emitAsync](#emitteremitasyncevent-arg1-arg2-) method to return the results of the listeners via Promise.all | ||
- Feature-rich [waitFor](#emitterwaitforevent-options) method to wait for events using promises | ||
- Extended version of the [events.once](#eventemitter2onceemitter-name-options) method from the [node events API](https://nodejs.org/api/events.html#events_events_once_emitter_name) | ||
- Browser & Workers environment compatibility | ||
- Demonstrates good performance in benchmarks | ||
@@ -286,2 +289,7 @@ | ||
### emitter.getMaxListeners() | ||
Returns the current max listener value for the EventEmitter which is either set by emitter.setMaxListeners(n) or defaults to EventEmitter2.defaultMaxListeners | ||
### emitter.listeners(event) | ||
@@ -351,2 +359,4 @@ | ||
### emitter.waitFor(event, [options]) | ||
### emitter.waitFor(event, [timeout]) | ||
### emitter.waitFor(event, [filter]) | ||
@@ -372,3 +382,5 @@ Returns a thenable object (promise interface) that resolves when a specific event occurs | ||
return arg0==='foo' && arg1==='bar' | ||
} | ||
}, | ||
Promise: Promise, // Promise constructor to use, | ||
overload: false // overload cancellation api in a case of external Promise class | ||
}).then(function(data){ | ||
@@ -382,9 +394,9 @@ console.log(data); // ['foo', 'bar'] | ||
````javascript | ||
var thenable= emitter.waitFor('event'); | ||
var promise= emitter.waitFor('event'); | ||
thenable.then(null, function(error){ | ||
promise.then(null, function(error){ | ||
console.log(error); //Error: canceled | ||
}); | ||
thenable.cancel(); //stop listening the event and reject the promise | ||
promise.cancel(); //stop listening the event and reject the promise | ||
```` | ||
@@ -412,1 +424,84 @@ | ||
``` | ||
### EventEmitter2.once(emitter, name, [options]) | ||
Creates a cancellable Promise that is fulfilled when the EventEmitter emits the given event or that is rejected | ||
when the EventEmitter emits 'error'. | ||
The Promise will resolve with an array of all the arguments emitted to the given event. | ||
This method is intentionally generic and works with the web platform EventTarget interface, | ||
which has no special 'error' event semantics and does not listen to the 'error' event. | ||
Basic example: | ||
````javascript | ||
var emitter= new EventEmitter2(); | ||
EventEmitter2.once(emitter, 'event', { | ||
timeout: 0, | ||
Promise: Promise, // a custom Promise constructor | ||
overload: false // overload promise cancellation api if exists with library implementation | ||
}).then(function(data){ | ||
console.log(data); // [1, 2, 3] | ||
}); | ||
emitter.emit('event', 1, 2, 3); | ||
```` | ||
With timeout option: | ||
````javascript | ||
EventEmitter2.once(emitter, 'event', { | ||
timeout: 1000 | ||
}).then(null, function(err){ | ||
console.log(err); // Error: timeout | ||
}); | ||
```` | ||
The library promise cancellation API: | ||
````javascript | ||
promise= EventEmitter2.once(emitter, 'event'); | ||
// notice: the cancel method exists only in the first promise chain | ||
promise.then(null, function(err){ | ||
console.log(err); // Error: canceled | ||
}); | ||
promise.cancel(); | ||
```` | ||
Using the custom Promise class (**[bluebird.js](https://www.npmjs.com/package/bluebird)**): | ||
````javascript | ||
var BBPromise = require("bluebird"); | ||
EventEmitter2.once(emitter, 'event', { | ||
Promise: BBPromise | ||
}).then(function(data){ | ||
console.log(data); // [4, 5, 6] | ||
}); | ||
emitter.emit('event', 4, 5, 6); | ||
```` | ||
````javascript | ||
var BBPromise = require("bluebird"); | ||
BBPromise.config({ | ||
// if false or options.overload enabled, the library cancellation API will be used | ||
cancellation: true | ||
}); | ||
var promise= EventEmitter2.once(emitter, 'event', { | ||
Promise: BBPromise, | ||
overload: false // use bluebird cancellation API | ||
}).then(function(data){ | ||
// notice: never executed due to BlueBird cancellation logic | ||
}, function(err){ | ||
// notice: never executed due to BlueBird cancellation logic | ||
}); | ||
promise.cancel(); | ||
emitter.emit('event', 'never handled'); | ||
```` | ||
### emitter.listeners(eventName) | ||
Returns the array of listeners for the event named eventName. | ||
### EventEmitter2.defaultMaxListeners | ||
Sets default max listeners count globally for all instances, including those created before the change is made. |
53741
1068
502
4