summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Noordhuis <info@bnoordhuis.nl>2012-08-01 00:57:15 +0200
committerBen Noordhuis <info@bnoordhuis.nl>2012-09-22 03:48:59 +0200
commit84221fd1d690070877adacd365ac6429b92bd838 (patch)
treee40dc326d651f9bc58833c07e396e7635a6f402c
parentd0e6c3f5a644e4f84e80e9081d28570bf7477391 (diff)
downloadnode-84221fd1d690070877adacd365ac6429b92bd838.tar.gz
events: add 'removeListener' event
-rw-r--r--doc/api/events.markdown2
-rw-r--r--lib/events.js23
-rw-r--r--test/simple/test-event-emitter-remove-all-listeners.js15
-rw-r--r--test/simple/test-event-emitter-remove-listeners.js29
4 files changed, 63 insertions, 6 deletions
diff --git a/doc/api/events.markdown b/doc/api/events.markdown
index 692efa815..7998684e7 100644
--- a/doc/api/events.markdown
+++ b/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.
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)
diff --git a/lib/events.js b/lib/events.js
index 9b26c5663..ee02f15af 100644
--- a/lib/events.js
+++ b/lib/events.js
@@ -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');
@@ -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;
};
diff --git a/test/simple/test-event-emitter-remove-all-listeners.js b/test/simple/test-event-emitter-remove-all-listeners.js
index 38cfb79c6..2cd31ebd2 100644
--- a/test/simple/test-event-emitter-remove-all-listeners.js
+++ b/test/simple/test-event-emitter-remove-all-listeners.js
@@ -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();
@@ -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]);
@@ -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'));
diff --git a/test/simple/test-event-emitter-remove-listeners.js b/test/simple/test-event-emitter-remove-listeners.js
index 70526668c..78133a4f3 100644
--- a/test/simple/test-event-emitter-remove-listeners.js
+++ b/test/simple/test-event-emitter-remove-listeners.js
@@ -23,7 +23,6 @@ var common = require('../common');
var assert = require('assert');
var events = require('events');
-
var count = 0;
function listener1() {
@@ -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);