Skip to content
This repository was archived by the owner on Apr 22, 2023. It is now read-only.

Commit 84221fd

Browse files
committed
events: add 'removeListener' event
1 parent d0e6c3f commit 84221fd

File tree

4 files changed

+63
-6
lines changed

4 files changed

+63
-6
lines changed

doc/api/events.markdown

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ If there is no listener for it, then the default action is to print a stack
2626
trace and exit the program.
2727

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

3131
### emitter.addListener(event, listener)
3232
### emitter.on(event, listener)

lib/events.js

+21-2
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ EventEmitter.prototype.once = function(type, listener) {
191191
return this;
192192
};
193193

194+
// emits a 'removeListener' event iff the listener was removed
194195
EventEmitter.prototype.removeListener = function(type, listener) {
195196
if ('function' !== typeof listener) {
196197
throw new Error('removeListener only takes instances of Function');
@@ -216,23 +217,41 @@ EventEmitter.prototype.removeListener = function(type, listener) {
216217
list.splice(position, 1);
217218
if (list.length == 0)
218219
delete this._events[type];
220+
this.emit('removeListener', type, listener);
219221
} else if (list === listener ||
220222
(list.listener && list.listener === listener))
221223
{
222224
delete this._events[type];
225+
this.emit('removeListener', type, listener);
223226
}
224227

225228
return this;
226229
};
227230

228231
EventEmitter.prototype.removeAllListeners = function(type) {
232+
if (!this._events) return this;
233+
229234
if (arguments.length === 0) {
235+
for (var key in this._events) {
236+
if (key === 'removeListener') continue;
237+
this.removeAllListeners(key);
238+
}
239+
this.removeAllListeners('removeListener');
230240
this._events = {};
231241
return this;
232242
}
233243

234-
// does not use listeners(), so no side effect of creating _events[type]
235-
if (type && this._events && this._events[type]) this._events[type] = null;
244+
var listeners = this._events[type];
245+
if (isArray(listeners)) {
246+
while (listeners.length) {
247+
// LIFO order
248+
this.removeListener(type, listeners[listeners.length - 1]);
249+
}
250+
} else if (listeners) {
251+
this.removeListener(type, listeners);
252+
}
253+
this._events[type] = null;
254+
236255
return this;
237256
};
238257

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

+15
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,17 @@ var assert = require('assert');
2424
var events = require('events');
2525

2626

27+
function expect(expected) {
28+
var actual = [];
29+
process.on('exit', function() {
30+
assert.deepEqual(actual.sort(), expected.sort());
31+
});
32+
function listener(name) {
33+
actual.push(name)
34+
}
35+
return common.mustCall(listener, expected.length);
36+
}
37+
2738
function listener() {}
2839

2940
var e1 = new events.EventEmitter();
@@ -34,6 +45,7 @@ e1.on('baz', listener);
3445
var fooListeners = e1.listeners('foo');
3546
var barListeners = e1.listeners('bar');
3647
var bazListeners = e1.listeners('baz');
48+
e1.on('removeListener', expect(['bar', 'baz', 'baz']));
3749
e1.removeAllListeners('bar');
3850
e1.removeAllListeners('baz');
3951
assert.deepEqual(e1.listeners('foo'), [listener]);
@@ -52,6 +64,9 @@ assert.notEqual(e1.listeners('baz'), bazListeners);
5264
var e2 = new events.EventEmitter();
5365
e2.on('foo', listener);
5466
e2.on('bar', listener);
67+
// expect LIFO order
68+
e2.on('removeListener', expect(['foo', 'bar', 'removeListener']));
69+
e2.on('removeListener', expect(['foo', 'bar']));
5570
e2.removeAllListeners();
5671
console.error(e2);
5772
assert.deepEqual([], e2.listeners('foo'));

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

+26-3
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ var common = require('../common');
2323
var assert = require('assert');
2424
var events = require('events');
2525

26-
2726
var count = 0;
2827

2928
function listener1() {
@@ -41,21 +40,45 @@ function listener3() {
4140
count++;
4241
}
4342

43+
function remove1() {
44+
assert(0);
45+
}
46+
47+
function remove2() {
48+
assert(0);
49+
}
50+
4451
var e1 = new events.EventEmitter();
4552
e1.on('hello', listener1);
53+
e1.on('removeListener', common.mustCall(function(name, cb) {
54+
assert.equal(name, 'hello');
55+
assert.equal(cb, listener1);
56+
}));
4657
e1.removeListener('hello', listener1);
4758
assert.deepEqual([], e1.listeners('hello'));
4859

4960
var e2 = new events.EventEmitter();
5061
e2.on('hello', listener1);
62+
e2.on('removeListener', assert.fail);
5163
e2.removeListener('hello', listener2);
5264
assert.deepEqual([listener1], e2.listeners('hello'));
5365

5466
var e3 = new events.EventEmitter();
5567
e3.on('hello', listener1);
5668
e3.on('hello', listener2);
69+
e3.on('removeListener', common.mustCall(function(name, cb) {
70+
assert.equal(name, 'hello');
71+
assert.equal(cb, listener1);
72+
}));
5773
e3.removeListener('hello', listener1);
5874
assert.deepEqual([listener2], e3.listeners('hello'));
5975

60-
61-
76+
var e4 = new events.EventEmitter();
77+
e4.on('removeListener', common.mustCall(function(name, cb) {
78+
if (cb !== remove1) return;
79+
this.removeListener('quux', remove2);
80+
this.emit('quux');
81+
}, 2));
82+
e4.on('quux', remove1);
83+
e4.on('quux', remove2);
84+
e4.removeListener('quux', remove1);

0 commit comments

Comments
 (0)