Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: yanickrochon/promise-events
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 3973a146bbd33de752d696d893bcb7bb3c05eda6
Choose a base ref
...
head repository: yanickrochon/promise-events
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 91191ec5552bdb360acf49a63543597dddfc4616
Choose a head ref

Commits on Jul 17, 2020

  1. Bump lodash from 4.17.15 to 4.17.19

    Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
    - [Release notes](https://github.com/lodash/lodash/releases)
    - [Commits](lodash/lodash@4.17.15...4.17.19)
    
    Signed-off-by: dependabot[bot] <support@github.com>
    dependabot[bot] authored Jul 17, 2020
    Copy the full SHA
    62dcec3 View commit details

Commits on Sep 4, 2020

  1. Bump yargs-parser from 18.1.1 to 18.1.3

    Bumps [yargs-parser](https://github.com/yargs/yargs-parser) from 18.1.1 to 18.1.3.
    - [Release notes](https://github.com/yargs/yargs-parser/releases)
    - [Changelog](https://github.com/yargs/yargs-parser/blob/master/CHANGELOG.md)
    - [Commits](yargs/yargs-parser@v18.1.1...v18.1.3)
    
    Signed-off-by: dependabot[bot] <support@github.com>
    dependabot[bot] authored Sep 4, 2020
    Copy the full SHA
    c624b46 View commit details

Commits on Sep 28, 2020

  1. Merge pull request #12 from yanickrochon/dependabot/npm_and_yarn/loda…

    …sh-4.17.19
    
    Bump lodash from 4.17.15 to 4.17.19
    yanickrochon authored Sep 28, 2020
    Copy the full SHA
    670a4a9 View commit details
  2. Merge pull request #13 from yanickrochon/dependabot/npm_and_yarn/yarg…

    …s-parser-18.1.3
    
    Bump yargs-parser from 18.1.1 to 18.1.3
    yanickrochon authored Sep 28, 2020
    Copy the full SHA
    83a0d21 View commit details
  3. Copy the full SHA
    e632eda View commit details

Commits on Sep 29, 2020

  1. WIP

    yanickrochon committed Sep 29, 2020
    Copy the full SHA
    d622f75 View commit details

Commits on Sep 30, 2020

  1. Copy the full SHA
    f5bd120 View commit details
  2. Copy the full SHA
    eeca987 View commit details
  3. Updated dev dependencies and implemented errorMonitor

    Using spread arguments and preparing to drop domain support.
    yanickrochon authored Sep 30, 2020
    Copy the full SHA
    7cdf315 View commit details

Commits on Nov 19, 2020

  1. chore(package): up deps

    antongolub committed Nov 19, 2020
    Copy the full SHA
    32f8615 View commit details
  2. feat: add basic TS libdefs

    antongolub committed Nov 19, 2020
    Copy the full SHA
    c156727 View commit details
  3. Copy the full SHA
    f758cc8 View commit details
  4. Merge pull request #17 from qiwi-forks/add-ts-libdefs

    feat: add basic TS libdefs
    yanickrochon authored Nov 19, 2020
    Copy the full SHA
    551972b View commit details
  5. Bumped version

    yanickrochon committed Nov 19, 2020
    Copy the full SHA
    1c411c1 View commit details

Commits on Nov 20, 2020

  1. Copy the full SHA
    38314a4 View commit details

Commits on Nov 28, 2020

  1. Minor fixes and libdef improvements (#18)

    * fix: add `off` alias for removeListener method
    
    * test: check events.listeners are synced with `_events`
    
    * test: check EventEmitter self-reference
    
    * test: verify events.listenerCount is synced with _eventsCount
    
    * refactor: tweak up `errorMonitor` emit promise
    
    * refactor: impove TS libdefs
    
    * chore: apply npm audit fix
    antongolub authored Nov 28, 2020
    Copy the full SHA
    6e2b63c View commit details
  2. Bumped version to 0.2.2

    yanickrochon committed Nov 28, 2020
    Copy the full SHA
    85373ef View commit details

Commits on Apr 7, 2021

  1. Fix types for resultFilter getter/setter (#21)

    * Fix types for resultFilter getter/setter
    
    * Fix type for TListener
    KoltesDigital authored Apr 7, 2021
    Copy the full SHA
    eb45c74 View commit details
  2. Bumped version 0.2.3

    yanickrochon committed Apr 7, 2021
    Copy the full SHA
    b001fa2 View commit details

Commits on Apr 27, 2021

  1. Copy the full SHA
    4865793 View commit details
  2. Copy the full SHA
    91191ec View commit details
Showing with 1,362 additions and 1,079 deletions.
  1. +3 −0 .npmignore
  2. +1 −1 .travis.yml
  3. +1 −0 README.md
  4. +33 −0 emitter.d.ts
  5. +42 −92 emitter.js
  6. +1,213 −977 package-lock.json
  7. +12 −3 package.json
  8. +2 −0 test/emit.test.js
  9. +37 −6 test/inheritance.test.js
  10. +17 −0 test/listeners.test.js
  11. +1 −0 test/prototype.test.js
3 changes: 3 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
test/
.gitignore
.travis.yml
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
language: node_js
node_js:
- "8"
- "10"
- "12"
- "14"
matrix:
fast_finish: true
script: "npm run test-cov"
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -94,6 +94,7 @@ Most of the implementation is fully compatible with the standard `EventEmitter`.
* [EventEmitter.listenerCount(emitter, eventName)](https://nodejs.org/api/events.html#events_eventemitter_listenercount_emitter_eventname) *deprecated*
* [EventEmitter.defaultMaxListeners](https://nodejs.org/api/events.html#events_eventemitter_defaultmaxlisteners)
* **EventEmitter.defaultResultFilter**
* [EventEmitter.errorMonitor](https://nodejs.org/api/events.html#events_eventemitter_errorMonitor)
* [emitter.addListener(eventName, listener)](https://nodejs.org/api/events.html#events_emitter_addlistener_eventname_listener)

Returns a `Promise` resolving when all `newListener` events have been emitted.
33 changes: 33 additions & 0 deletions emitter.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
declare type TEventType = string | symbol;
declare type TListener = (...args: any[]) => Promise<any>;
declare type TFilter<T = any> = {
<S extends T>(callbackfn: (value: T, index: number, array: T[]) => value is S, thisArg?: any): S[];
(callbackfn: (value: T, index: number, array: T[]) => unknown, thisArg?: any): T[];
};
declare class EventEmitter {
private _resultFilter;
private _events;
private _eventsCount;
private _domain;
get maxListeners(): number;
set maxListeners(n: number);
getResultFilter(): TFilter | undefined;
setResultFilter(filter: TFilter | undefined): this;
get resultFilter(): TFilter | undefined;
set resultFilter(filter: TFilter | undefined);
emit(type: TEventType, ...args: any[]): Promise<any>;
addListener(type: TEventType, listener: TListener): Promise<any>;
prependListener(type: TEventType, listener: TListener): Promise<any>;
once(type: TEventType, listener?: TListener): Promise<any>;
prependOnceListener(type: TEventType, listener: TListener): Promise<any>;
removeListener(type: TEventType, listener: TListener): Promise<any>;
removeAllListeners(type: string | symbol): Promise<any>;
on(type: TEventType, listener: TListener): Promise<any>;
off(type: TEventType, listener: TListener): Promise<any>;

static EventEmitter: typeof EventEmitter;
static defaultMaxListeners: number | undefined;
static usingDomains: boolean | undefined;
static listenerCount: (events: EventEmitter, type: TEventType) => number;
}
export = EventEmitter;
134 changes: 42 additions & 92 deletions emitter.js
Original file line number Diff line number Diff line change
@@ -2,8 +2,6 @@

const events = require('events');

let handlerProcessor = sequentialHandlerProcessor;


class EventEmitter extends events.EventEmitter {
constructor() {
@@ -45,56 +43,61 @@ class EventEmitter extends events.EventEmitter {
}


emit(type) {
emit(type, ...args) {
// keep a reference to _resultFilter since the filter function
// could theoretically set a new result filter, leading to
// undefined results
const resultFilter = this.getResultFilter();
let er, handlers, len, args, events, domain;
const events = this._events;
const domain = this._domain;
const errorMonitor = EventEmitter.errorMonitor;
const results = [];
let handlers;
let needDomainExit = false;
let doError = (type === 'error');
let emitter = this;
let promise;

events = this._events;
//let emitter = this;
let promise = Promise.resolve();

if (events) {
doError = (doError && events.error == null);
if (doError && (events[errorMonitor] !== undefined)) {
promise = promise.then(() => this.emit(errorMonitor, ...args));
}
doError = (doError && events.error === undefined);
} else if (!doError) {
return Promise.resolve();
return promise.then(() => false);
}

domain = this.domain;

// If there is no 'error' event listener then reject
if (doError) {
er = arguments[1];

if (er) {
if (!(er instanceof Error)) {
// At least give some kind of context to the user
let err = new Error('Uncaught, unspecified "error" event. (' + er + ')');
err.context = er;
er = err;
}
} else {
let er;
if (args.length > 0) {
er = args[0];
}
if (!er) {
er = new Error('Uncaught, unspecified "error" event.');
} else if (!(er instanceof Error)) {
// At least give some kind of context to the user
let err = new Error('Uncaught, unspecified "error" event. (' + er + ')');
err.context = er;
er = err;
}

// @deprecated
// TODO : remove domains
if (domain) {
er.domainEmitter = this;
er.domain = domain;
er.domainThrown = false;
domain.emit('error', er);
promise = promise.then(() => domain.emit('error', er));
}

return Promise.reject(er);
return promise.then(() => { throw er; });
}

handlers = events[type];

handlers = events && events[type];

if (!handlers) {
return Promise.resolve();
return promise;
}

if (domain && this !== process) {
@@ -104,47 +107,17 @@ class EventEmitter extends events.EventEmitter {

if (typeof handlers === 'function') {
handlers = [handlers];
} else {
handlers = handlers.slice();
}

len = arguments.length;
switch (len) {
// fast cases
case 1:
promise = handlerProcessor(handlers, handler => handler.call(emitter));
break;
case 2:
args = arguments;
promise = handlerProcessor(handlers, handler => handler.call(emitter, args[1]));
break;
case 3:
args = arguments;
promise = handlerProcessor(handlers, handler => handler.call(emitter, args[1], args[2]));
break;
case 4:
args = arguments;
promise = handlerProcessor(handlers, handler => handler.call(emitter, args[1], args[2], args[3]));
break;
// slower
default:
args = new Array(len - 1);
for (let i = 1; i < len; ++i) {
args[i - 1] = arguments[i];
}
promise = handlerProcessor(handlers, handler => handler.apply(emitter, args));
if (handlers.length) {
promise = handlers.reduce((p, handler) => p.then(() => handler(...args)).then(result => results.push(result)), promise);
}

if (needDomainExit) {
promise.then(() => domain.exit());
promise = promise.then(() => domain.exit(), err => { domain.exit(); throw err; });
}

if (!resultFilter) {
// unfiltered version
return promise;
} else {
return promise.then(results => results.filter(resultFilter));
}
return promise.then(() => resultFilter ? results.filter(resultFilter) : results);
}


@@ -181,7 +154,6 @@ class EventEmitter extends events.EventEmitter {

removeListener(type, listener) {
let list, events, position;
let promise;

if (typeof listener !== 'function') {
throw new TypeError('"listener" argument must be a function');
@@ -201,7 +173,7 @@ class EventEmitter extends events.EventEmitter {
delete events[type];

if (events.removeListener) {
promise = this.emit('removeListener', type, listener);
return this.emit('removeListener', type, listener);
}
}
} else if (typeof list !== 'function') {
@@ -240,11 +212,11 @@ class EventEmitter extends events.EventEmitter {
--this._eventsCount;

if (events.removeListener) {
promise = this.emit('removeListener', type, listener);
return this.emit('removeListener', type, listener);
}
}

return promise || Promise.resolve();
return Promise.resolve();
}


@@ -284,10 +256,10 @@ class EventEmitter extends events.EventEmitter {
key = keys[i];
if (key === 'removeListener') continue;

promise = promise && promise.then(this.removeAllListeners(key)) || this.removeAllListeners(key);
promise = promise ? promise.then(this.removeAllListeners(key)) : this.removeAllListeners(key);
}

promise = promise && promise.then(this.removeAllListeners('removeListener')) || this.removeAllListeners('removeListener');
promise = promise ? promise.then(this.removeAllListeners('removeListener')) : this.removeAllListeners('removeListener');
this._events = {};
this._eventsCount = 0;

@@ -301,7 +273,7 @@ class EventEmitter extends events.EventEmitter {
} else if (listeners) {
// LIFO order
for (let i = listeners.length - 1; i >= 0; --i) {
promise = promise && promise.then(this.removeListener(type, listeners[i])) || this.removeListener(type, listeners[i]);
promise = promise ? promise.then(this.removeListener(type, listeners[i])) : this.removeListener(type, listeners[i]);
}
}

@@ -329,37 +301,15 @@ Object.defineProperties(EventEmitter, {
set: function setUsingDomains(b) {
events.EventEmitter.usingDomains = b;
}
},
//sequentialHandlers: {
// get: function getSequentialHandlers() {
// return handlerProcessor === sequentialHandlerProcessor;
// },
// set: function setSequentialHandlers(b) {
// handlerProcessor = b ? sequentialHandlerProcessor : concurrentHandlerProcessor;
// }
//}
}
});

EventEmitter.defaultResultFilter = undefined;
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
EventEmitter.prototype._resultFilter = undefined;


function sequentialHandlerProcessor(handlers, callback) {
let results = [];
return handlers.reduce((promise, handler) => promise.then(() => callback(handler)).then(result => results.push(result)), Promise.resolve()).then(() => results);
}

//function concurrentHandlerProcessor(handlers, callback) {
// return Promise.all(handlers.map(handler => {
// try {
// return callback(handler);
// } catch (err) {
// return Promise.reject(err);
// }
// }));
//}


function _addListener(target, type, listener, prepend) {
let m;
2,190 changes: 1,213 additions & 977 deletions package-lock.json

Large diffs are not rendered by default.

15 changes: 12 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "promise-events",
"version": "0.1.8",
"version": "0.2.4",
"description": "A promise-based events emitter",
"author": "Yanick Rochon <yanick.rochon@gmail.com>",
"keywords": [
@@ -11,8 +11,14 @@
],
"license": "MIT",
"main": "emitter.js",
"files": [
"emitter.js",
"emitter.d.ts",
"README.md",
"LICENSE"
],
"scripts": {
"test": "jest ./test",
"test": "jest --detectOpenHandles --forceExit",
"test-cov": "npm run test -- --coverage"
},
"homepage": "https://github.com/yanickrochon/promise-events#readme",
@@ -24,9 +30,12 @@
"url": "https://github.com/yanickrochon/promise-events/issues"
},
"devDependencies": {
"jest": "^25.1.0"
"jest": "^26.6.3"
},
"engines": {
"node": ">=8.0.0"
},
"jest": {
"testEnvironment": "node"
}
}
2 changes: 2 additions & 0 deletions test/emit.test.js
Original file line number Diff line number Diff line change
@@ -61,6 +61,7 @@ describe("Test emitting events", () => {

expect( results ).toHaveLength(1);
expect( Emitter.listenerCount(events, 'foo') ).toEqual(1);
expect( events.listenerCount('foo') ).toEqual(1);

return events.on('foo', fn).then(() => {
expect( events.listeners('foo') ).toHaveLength(2);
@@ -69,6 +70,7 @@ describe("Test emitting events", () => {

expect( results ).toHaveLength(2);
expect( Emitter.listenerCount(events, 'foo') ).toEqual(2);
expect( events.listenerCount('foo') ).toEqual(2);

});
});
43 changes: 37 additions & 6 deletions test/inheritance.test.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@

'use strict';

describe("Test inheritance", function () {
describe("Test inheritance", () => {

const Emitter = require('../emitter');
const NativeEmitter = require('events').EventEmitter;


it("should create valid instance", function () {
it("should create valid instance", () => {
const util = require('util');
const SubEmitter = function () {};

@@ -33,12 +34,42 @@ describe("Test inheritance", function () {
});


it('should be instance of built-in EventEmitter', function () {
const EventEmitter = require('events').EventEmitter;
it('should be instance of built-in EventEmitter', () => {
const events = new Emitter();

let events = new Emitter();
expect( events ).toBeInstanceOf( NativeEmitter );
});


it('should return valid event names', async () => {
const events = new Emitter();

await events.addListener('test', () => {});

expect( events.eventNames() ).toEqual(['test']);
});


it('should use error monitor', async () => {
if (Emitter.errorMonitor) {
const events = new Emitter();
const error = new Error('test');

let errorMonitored = null;
let errorEmitted = null;

await events.addListener(Emitter.errorMonitor, err => errorMonitored = err);
await events.addListener('error', err => errorEmitted = err);

await events.emit('error', error);

expect( errorMonitored ).toBe(error);
expect( errorEmitted ).toBe(error);
}
});

expect( events ).toBeInstanceOf( EventEmitter );
it("Emitter should provide self reference", function () {
expect(Emitter.EventEmitter).toBe(Emitter)
});

});
17 changes: 17 additions & 0 deletions test/listeners.test.js
Original file line number Diff line number Diff line change
@@ -5,34 +5,51 @@ describe("Test adding and removing listeners", function () {


it("should add and remove listeners", function () {
const asArray = (value) => Array.isArray(value) ? value : [value];
const events = new Emitter();
let fn = function () {};

expect( events ).toHaveProperty('_eventsCount', 0);
expect( events.listenerCount('foo') ).toEqual(0);
expect( events.listeners() ).toEqual([])

return events.addListener('foo', fn).then(() => {
expect( events ).toHaveProperty('_eventsCount', 1);
expect( events.listenerCount('foo') ).toEqual(1);
expect( events._events ).toHaveProperty('foo', fn);
expect( events.listeners('foo') ).toEqual(asArray(events._events['foo']))
expect( events.rawListeners('foo') ).toEqual(asArray(events._events['foo']))

return events.addListener('foo', fn);
}).then(() => {
expect( events ).toHaveProperty('_eventsCount', 2);
expect( events.listenerCount('foo') ).toEqual(2);
expect( events._events ).toHaveProperty('foo', [fn, fn]);
expect( events.listeners('foo') ).toEqual(asArray(events._events['foo']))
expect( events.rawListeners('foo') ).toEqual(asArray(events._events['foo']))

return events.removeListener('foo', () => {}); // unknown listener
}).then(() => {
expect( events ).toHaveProperty('_eventsCount', 2);
expect( events.listenerCount('foo') ).toEqual(2);
expect( events._events ).toHaveProperty('foo', [fn, fn]);
expect( events.listeners('foo') ).toEqual(asArray(events._events['foo']))

return events.removeListener('foo', fn);
}).then(() => {
expect( events ).toHaveProperty('_eventsCount', 1);
expect( events.listenerCount('foo') ).toEqual(1);
expect( events._events ).toHaveProperty('foo', fn);
expect( events.listeners('foo') ).toEqual(asArray(events._events['foo']))
expect( events.rawListeners('foo') ).toEqual(asArray(events._events['foo']))

return events.removeListener('foo', fn);
}).then(() => {
expect( events ).toHaveProperty('_eventsCount', 0);
expect( events.listenerCount('foo') ).toEqual(0);
expect( events._events ).toEqual({});
expect( events.listeners('foo') ).toEqual([])
expect( events.rawListeners('foo') ).toEqual([])

return events.removeListener('buz', fn);
});
1 change: 1 addition & 0 deletions test/prototype.test.js
Original file line number Diff line number Diff line change
@@ -37,6 +37,7 @@ describe("Test EventEmitter prototype", function () {
let descriptor;

expect( Emitter.prototype.on ).toBe( Emitter.prototype.addListener );
expect( Emitter.prototype.off ).toBe( Emitter.prototype.removeListener );
expect( Emitter.prototype ).toHaveProperty('maxListeners');

descriptor = Object.getOwnPropertyDescriptor(Emitter.prototype, 'maxListeners');