diff options
author | Ben Noordhuis <info@bnoordhuis.nl> | 2012-08-01 00:57:15 +0200 |
---|---|---|
committer | Ben Noordhuis <info@bnoordhuis.nl> | 2012-09-22 03:48:59 +0200 |
commit | 84221fd1d690070877adacd365ac6429b92bd838 (patch) | |
tree | e40dc326d651f9bc58833c07e396e7635a6f402c | |
parent | d0e6c3f5a644e4f84e80e9081d28570bf7477391 (diff) | |
download | node-84221fd1d690070877adacd365ac6429b92bd838.tar.gz |
events: add 'removeListener' event
-rw-r--r-- | doc/api/events.markdown | 2 | ||||
-rw-r--r-- | lib/events.js | 23 | ||||
-rw-r--r-- | test/simple/test-event-emitter-remove-all-listeners.js | 15 | ||||
-rw-r--r-- | test/simple/test-event-emitter-remove-listeners.js | 29 |
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); |