Skip to content

Commit c814c7e

Browse files
lpincaitaloacasas
authored andcommitted
events: do not keep arrays with a single listener
Use the remaining listener directly if the array of listeners has only one element after running `EventEmitter.prototype.removeListener()`. Advantages: - Better memory usage and better performance if no new listeners are added for the same event. Disadvantages: - A new array must be created if new listeners are added for the same event. PR-URL: #12043 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Daijiro Wachi <[email protected]> Reviewed-By: Ron Korving <[email protected]>
1 parent 8058bae commit c814c7e

File tree

2 files changed

+28
-7
lines changed

2 files changed

+28
-7
lines changed

lib/events.js

+11-7
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ EventEmitter.prototype.removeListener =
344344
} else if (typeof list !== 'function') {
345345
position = -1;
346346

347-
for (i = list.length; i-- > 0;) {
347+
for (i = list.length - 1; i >= 0; i--) {
348348
if (list[i] === listener || list[i].listener === listener) {
349349
originalListener = list[i].listener;
350350
position = i;
@@ -356,7 +356,6 @@ EventEmitter.prototype.removeListener =
356356
return this;
357357

358358
if (list.length === 1) {
359-
list[0] = undefined;
360359
if (--this._eventsCount === 0) {
361360
this._events = new EventHandlers();
362361
return this;
@@ -365,8 +364,12 @@ EventEmitter.prototype.removeListener =
365364
}
366365
} else if (position === 0) {
367366
list.shift();
367+
if (list.length === 1)
368+
events[type] = list[0];
368369
} else {
369370
spliceOne(list, position);
371+
if (list.length === 1)
372+
events[type] = list[0];
370373
}
371374

372375
if (events.removeListener)
@@ -378,7 +381,7 @@ EventEmitter.prototype.removeListener =
378381

379382
EventEmitter.prototype.removeAllListeners =
380383
function removeAllListeners(type) {
381-
var listeners, events;
384+
var listeners, events, i;
382385

383386
events = this._events;
384387
if (!events)
@@ -401,7 +404,8 @@ EventEmitter.prototype.removeAllListeners =
401404
// emit removeListener for all listeners on all events
402405
if (arguments.length === 0) {
403406
var keys = Object.keys(events);
404-
for (var i = 0, key; i < keys.length; ++i) {
407+
var key;
408+
for (i = 0; i < keys.length; ++i) {
405409
key = keys[i];
406410
if (key === 'removeListener') continue;
407411
this.removeAllListeners(key);
@@ -418,9 +422,9 @@ EventEmitter.prototype.removeAllListeners =
418422
this.removeListener(type, listeners);
419423
} else if (listeners) {
420424
// LIFO order
421-
do {
422-
this.removeListener(type, listeners[listeners.length - 1]);
423-
} while (listeners[0]);
425+
for (i = listeners.length - 1; i >= 0; i--) {
426+
this.removeListener(type, listeners[i]);
427+
}
424428
}
425429

426430
return this;

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

+17
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,20 @@ assert.throws(() => {
136136
const e = ee.removeListener('foo', listener);
137137
assert.strictEqual(e, ee);
138138
}
139+
140+
{
141+
const ee = new EventEmitter();
142+
143+
ee.on('foo', listener1);
144+
ee.on('foo', listener2);
145+
assert.deepStrictEqual(ee.listeners('foo'), [listener1, listener2]);
146+
147+
ee.removeListener('foo', listener1);
148+
assert.strictEqual(ee._events.foo, listener2);
149+
150+
ee.on('foo', listener1);
151+
assert.deepStrictEqual(ee.listeners('foo'), [listener2, listener1]);
152+
153+
ee.removeListener('foo', listener1);
154+
assert.strictEqual(ee._events.foo, listener2);
155+
}

0 commit comments

Comments
 (0)