Skip to content

Commit

Permalink
events: add 'removeListener' event
Browse files Browse the repository at this point in the history
  • Loading branch information
bnoordhuis committed Sep 22, 2012
1 parent d0e6c3f commit 84221fd
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 6 deletions.
2 changes: 1 addition & 1 deletion doc/api/events.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ If there is no listener for it, then the default action is to print a stack
trace and exit the program.

All EventEmitters emit the event `'newListener'` when new listeners are
added.
added and `'removeListener'` when a listener is removed.

### emitter.addListener(event, listener)
### emitter.on(event, listener)
Expand Down
23 changes: 21 additions & 2 deletions lib/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ EventEmitter.prototype.once = function(type, listener) {
return this;
};

// emits a 'removeListener' event iff the listener was removed
EventEmitter.prototype.removeListener = function(type, listener) {
if ('function' !== typeof listener) {
throw new Error('removeListener only takes instances of Function');
Expand All @@ -216,23 +217,41 @@ EventEmitter.prototype.removeListener = function(type, listener) {
list.splice(position, 1);
if (list.length == 0)
delete this._events[type];
this.emit('removeListener', type, listener);
} else if (list === listener ||
(list.listener && list.listener === listener))
{
delete this._events[type];
this.emit('removeListener', type, listener);
}

return this;
};

EventEmitter.prototype.removeAllListeners = function(type) {
if (!this._events) return this;

if (arguments.length === 0) {
for (var key in this._events) {
if (key === 'removeListener') continue;
this.removeAllListeners(key);
}
this.removeAllListeners('removeListener');
this._events = {};
return this;
}

// does not use listeners(), so no side effect of creating _events[type]
if (type && this._events && this._events[type]) this._events[type] = null;
var listeners = this._events[type];
if (isArray(listeners)) {
while (listeners.length) {
// LIFO order
this.removeListener(type, listeners[listeners.length - 1]);
}
} else if (listeners) {
this.removeListener(type, listeners);
}
this._events[type] = null;

return this;
};

Expand Down
15 changes: 15 additions & 0 deletions test/simple/test-event-emitter-remove-all-listeners.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ var assert = require('assert');
var events = require('events');


function expect(expected) {
var actual = [];
process.on('exit', function() {
assert.deepEqual(actual.sort(), expected.sort());
});
function listener(name) {
actual.push(name)
}
return common.mustCall(listener, expected.length);
}

function listener() {}

var e1 = new events.EventEmitter();
Expand All @@ -34,6 +45,7 @@ e1.on('baz', listener);
var fooListeners = e1.listeners('foo');
var barListeners = e1.listeners('bar');
var bazListeners = e1.listeners('baz');
e1.on('removeListener', expect(['bar', 'baz', 'baz']));
e1.removeAllListeners('bar');
e1.removeAllListeners('baz');
assert.deepEqual(e1.listeners('foo'), [listener]);
Expand All @@ -52,6 +64,9 @@ assert.notEqual(e1.listeners('baz'), bazListeners);
var e2 = new events.EventEmitter();
e2.on('foo', listener);
e2.on('bar', listener);
// expect LIFO order
e2.on('removeListener', expect(['foo', 'bar', 'removeListener']));
e2.on('removeListener', expect(['foo', 'bar']));
e2.removeAllListeners();
console.error(e2);
assert.deepEqual([], e2.listeners('foo'));
Expand Down
29 changes: 26 additions & 3 deletions test/simple/test-event-emitter-remove-listeners.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ var common = require('../common');
var assert = require('assert');
var events = require('events');


var count = 0;

function listener1() {
Expand All @@ -41,21 +40,45 @@ function listener3() {
count++;
}

function remove1() {
assert(0);
}

function remove2() {
assert(0);
}

var e1 = new events.EventEmitter();
e1.on('hello', listener1);
e1.on('removeListener', common.mustCall(function(name, cb) {
assert.equal(name, 'hello');
assert.equal(cb, listener1);
}));
e1.removeListener('hello', listener1);
assert.deepEqual([], e1.listeners('hello'));

var e2 = new events.EventEmitter();
e2.on('hello', listener1);
e2.on('removeListener', assert.fail);
e2.removeListener('hello', listener2);
assert.deepEqual([listener1], e2.listeners('hello'));

var e3 = new events.EventEmitter();
e3.on('hello', listener1);
e3.on('hello', listener2);
e3.on('removeListener', common.mustCall(function(name, cb) {
assert.equal(name, 'hello');
assert.equal(cb, listener1);
}));
e3.removeListener('hello', listener1);
assert.deepEqual([listener2], e3.listeners('hello'));



var e4 = new events.EventEmitter();
e4.on('removeListener', common.mustCall(function(name, cb) {
if (cb !== remove1) return;
this.removeListener('quux', remove2);
this.emit('quux');
}, 2));
e4.on('quux', remove1);
e4.on('quux', remove2);
e4.removeListener('quux', remove1);

0 comments on commit 84221fd

Please sign in to comment.