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

[api] Add removeListenersByContext(context) method #178

Open
wants to merge 1 commit 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ differences:
- The `setMaxListeners`, `getMaxListeners`, `prependListener` and
`prependOnceListener` methods are not available.
- Support for custom context for events so there is no need to use `fn.bind`.
- There is an additional method named `removeListenersByContext`, which removes all listeners of the given
context.
- The `removeListener` method removes all matching listeners, not only the
first.

Expand Down Expand Up @@ -61,6 +63,9 @@ or `this` value that should be set for the emitted events. This means you no
longer have the overhead of an event that required `fn.bind` in order to get a
custom `this` value.

In addition to that, we have added a new method: `EventEmitter.removeListenersByContext`,
which will remove all listeners of the given context.

```js
var EE = new EventEmitter()
, context = { foo: 'bar' };
Expand All @@ -72,6 +77,7 @@ function emitted() {
EE.once('event-name', emitted, context);
EE.on('another-event', emitted, context);
EE.removeListener('another-event', emitted, context);
EE.removeListenersByContext(context);
```

### Tests and benchmarks
Expand Down
5 changes: 5 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ declare class EventEmitter<EventTypes extends string | symbol = string | symbol>
* Remove all listeners, or those of the specified event.
*/
removeAllListeners(event?: EventTypes): this;

/**
* Removes all listeners that were added with the specified context.
*/
removeListenersByContext(context?: any): this;
}

declare namespace EventEmitter {
Expand Down
32 changes: 32 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,38 @@ EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {
return this;
};

/**
* Remove all listeners on a specific context.
*
* @param {*} context Only remove the listeners that have this context.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter.prototype.removeListenersByContext = function removeListenersByContext(context) {
var eventNames = this.eventNames();
var totalListenerCount = 0;

for (var i = 0, eventsCount = eventNames.length; i < eventsCount; i++) {
var evt = eventNames[i];
var listeners = this._events[evt];

if (listeners.fn) listeners = [listeners];
for (var j = 0, events = [], listenersCount = listeners.length; j < listenersCount; j++) {
if (listeners[j].context !== context) events.push(listeners[j]);
}

if (events.length) {
this._events[evt] = events.length === 1 ? events[0] : events;
totalListenerCount += events.length;
} else {
clearEvent(this, evt);
}
}

this._eventsCount = totalListenerCount;
return this;
};

//
// Alias methods names because people roll like that.
//
Expand Down
29 changes: 29 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,35 @@ describe('EventEmitter', function tests() {
});
});

describe('EventEmitter#removeListenersByContext', function () {
it('removes all listeners for the specified context', function () {
var e = new EventEmitter();
var ctx1 = {};
var ctx2 = {};
var ctx3 = {};

e.on('aaa', function () { throw new Error('oops'); }, ctx1);
e.on('bbb', function () { throw new Error('oops'); }, ctx1);
e.on('aaa', function () { throw new Error('oops'); }, ctx2);
e.on('bbb', function () { throw new Error('oops'); }, ctx3);

assume(e.removeListenersByContext(ctx1)).equals(e);
assume(e.listeners('aaa').length).equals(1);
assume(e.listeners('bbb').length).equals(1);
assume(e._eventsCount).equals(2);

assume(e.removeListenersByContext(ctx2)).equals(e);
assume(e.listeners('aaa').length).equals(0);
assume(e.listeners('bbb').length).equals(1);
assume(e._eventsCount).equals(1);

assume(e.removeListenersByContext(ctx3)).equals(e);
assume(e.listeners('aaa').length).equals(0);
assume(e.listeners('bbb').length).equals(0);
assume(e._eventsCount).equals(0);
});
});

describe('EventEmitter#eventNames', function () {
it('returns an empty array when there are no events', function () {
var e = new EventEmitter();
Expand Down