Browse Source

events: add 'removeListener' event

v0.9.3-release
Ben Noordhuis 13 years ago
parent
commit
84221fd1d6
  1. 2
      doc/api/events.markdown
  2. 23
      lib/events.js
  3. 15
      test/simple/test-event-emitter-remove-all-listeners.js
  4. 29
      test/simple/test-event-emitter-remove-listeners.js

2
doc/api/events.markdown

@ -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. trace and exit the program.
All EventEmitters emit the event `'newListener'` when new listeners are All EventEmitters emit the event `'newListener'` when new listeners are
added. added and `'removeListener'` when a listener is removed.
### emitter.addListener(event, listener) ### emitter.addListener(event, listener)
### emitter.on(event, listener) ### emitter.on(event, listener)

23
lib/events.js

@ -191,6 +191,7 @@ EventEmitter.prototype.once = function(type, listener) {
return this; return this;
}; };
// emits a 'removeListener' event iff the listener was removed
EventEmitter.prototype.removeListener = function(type, listener) { EventEmitter.prototype.removeListener = function(type, listener) {
if ('function' !== typeof listener) { if ('function' !== typeof listener) {
throw new Error('removeListener only takes instances of Function'); throw new Error('removeListener only takes instances of Function');
@ -216,23 +217,41 @@ EventEmitter.prototype.removeListener = function(type, listener) {
list.splice(position, 1); list.splice(position, 1);
if (list.length == 0) if (list.length == 0)
delete this._events[type]; delete this._events[type];
this.emit('removeListener', type, listener);
} else if (list === listener || } else if (list === listener ||
(list.listener && list.listener === listener)) (list.listener && list.listener === listener))
{ {
delete this._events[type]; delete this._events[type];
this.emit('removeListener', type, listener);
} }
return this; return this;
}; };
EventEmitter.prototype.removeAllListeners = function(type) { EventEmitter.prototype.removeAllListeners = function(type) {
if (!this._events) return this;
if (arguments.length === 0) { if (arguments.length === 0) {
for (var key in this._events) {
if (key === 'removeListener') continue;
this.removeAllListeners(key);
}
this.removeAllListeners('removeListener');
this._events = {}; this._events = {};
return this; return this;
} }
// does not use listeners(), so no side effect of creating _events[type] var listeners = this._events[type];
if (type && this._events && this._events[type]) this._events[type] = null; 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; return this;
}; };

15
test/simple/test-event-emitter-remove-all-listeners.js

@ -24,6 +24,17 @@ var assert = require('assert');
var events = require('events'); 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() {} function listener() {}
var e1 = new events.EventEmitter(); var e1 = new events.EventEmitter();
@ -34,6 +45,7 @@ e1.on('baz', listener);
var fooListeners = e1.listeners('foo'); var fooListeners = e1.listeners('foo');
var barListeners = e1.listeners('bar'); var barListeners = e1.listeners('bar');
var bazListeners = e1.listeners('baz'); var bazListeners = e1.listeners('baz');
e1.on('removeListener', expect(['bar', 'baz', 'baz']));
e1.removeAllListeners('bar'); e1.removeAllListeners('bar');
e1.removeAllListeners('baz'); e1.removeAllListeners('baz');
assert.deepEqual(e1.listeners('foo'), [listener]); assert.deepEqual(e1.listeners('foo'), [listener]);
@ -52,6 +64,9 @@ assert.notEqual(e1.listeners('baz'), bazListeners);
var e2 = new events.EventEmitter(); var e2 = new events.EventEmitter();
e2.on('foo', listener); e2.on('foo', listener);
e2.on('bar', listener); e2.on('bar', listener);
// expect LIFO order
e2.on('removeListener', expect(['foo', 'bar', 'removeListener']));
e2.on('removeListener', expect(['foo', 'bar']));
e2.removeAllListeners(); e2.removeAllListeners();
console.error(e2); console.error(e2);
assert.deepEqual([], e2.listeners('foo')); assert.deepEqual([], e2.listeners('foo'));

29
test/simple/test-event-emitter-remove-listeners.js

@ -23,7 +23,6 @@ var common = require('../common');
var assert = require('assert'); var assert = require('assert');
var events = require('events'); var events = require('events');
var count = 0; var count = 0;
function listener1() { function listener1() {
@ -41,21 +40,45 @@ function listener3() {
count++; count++;
} }
function remove1() {
assert(0);
}
function remove2() {
assert(0);
}
var e1 = new events.EventEmitter(); var e1 = new events.EventEmitter();
e1.on('hello', listener1); e1.on('hello', listener1);
e1.on('removeListener', common.mustCall(function(name, cb) {
assert.equal(name, 'hello');
assert.equal(cb, listener1);
}));
e1.removeListener('hello', listener1); e1.removeListener('hello', listener1);
assert.deepEqual([], e1.listeners('hello')); assert.deepEqual([], e1.listeners('hello'));
var e2 = new events.EventEmitter(); var e2 = new events.EventEmitter();
e2.on('hello', listener1); e2.on('hello', listener1);
e2.on('removeListener', assert.fail);
e2.removeListener('hello', listener2); e2.removeListener('hello', listener2);
assert.deepEqual([listener1], e2.listeners('hello')); assert.deepEqual([listener1], e2.listeners('hello'));
var e3 = new events.EventEmitter(); var e3 = new events.EventEmitter();
e3.on('hello', listener1); e3.on('hello', listener1);
e3.on('hello', listener2); e3.on('hello', listener2);
e3.on('removeListener', common.mustCall(function(name, cb) {
assert.equal(name, 'hello');
assert.equal(cb, listener1);
}));
e3.removeListener('hello', listener1); e3.removeListener('hello', listener1);
assert.deepEqual([listener2], e3.listeners('hello')); 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);

Loading…
Cancel
Save