mirror of
https://github.com/nodejs/node.git
synced 2026-10-14 10:26:26 -04:00
events: avoid cloning listeners array on every emit
PR-URL: https://github.com/nodejs/node/pull/62261 Reviewed-By: Chemi Atlow <chemi@atlow.co.il> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
This commit is contained in:
1 parent
adac077484
commit
7ffbb768fe
2 files changed
+62
-17
No files matched your search
+44
-17
@@ -87,6 +87,7 @@ const { addAbortListener } = require('internal/events/abort_listener');
|
||||
const kCapture = Symbol('kCapture');
|
||||
const kErrorMonitor = Symbol('events.errorMonitor');
|
||||
const kShapeMode = Symbol('shapeMode');
|
||||
const kEmitting = Symbol('events.emitting');
|
||||
const kMaxEventTargetListeners = Symbol('events.maxEventTargetListeners');
|
||||
const kMaxEventTargetListenersWarned =
|
||||
Symbol('events.maxEventTargetListenersWarned');
|
||||
@@ -514,19 +515,22 @@ EventEmitter.prototype.emit = function emit(type, ...args) {
|
||||
addCatch(this, result, type, args);
|
||||
}
|
||||
} else {
|
||||
const len = handler.length;
|
||||
const listeners = arrayClone(handler);
|
||||
for (let i = 0; i < len; ++i) {
|
||||
const result = ReflectApply(listeners[i], this, args);
|
||||
handler[kEmitting]++;
|
||||
try {
|
||||
for (let i = 0; i < handler.length; ++i) {
|
||||
const result = ReflectApply(handler[i], this, args);
|
||||
|
||||
// We check if result is undefined first because that
|
||||
// is the most common case so we do not pay any perf
|
||||
// penalty.
|
||||
// This code is duplicated because extracting it away
|
||||
// would make it non-inlineable.
|
||||
if (result !== undefined && result !== null) {
|
||||
addCatch(this, result, type, args);
|
||||
// We check if result is undefined first because that
|
||||
// is the most common case so we do not pay any perf
|
||||
// penalty.
|
||||
// This code is duplicated because extracting it away
|
||||
// would make it non-inlineable.
|
||||
if (result !== undefined && result !== null) {
|
||||
addCatch(this, result, type, args);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
handler[kEmitting]--;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -565,13 +569,17 @@ function _addListener(target, type, listener, prepend) {
|
||||
} else {
|
||||
if (typeof existing === 'function') {
|
||||
// Adding the second element, need to change to array.
|
||||
existing = events[type] =
|
||||
prepend ? [listener, existing] : [existing, listener];
|
||||
existing = prepend ? [listener, existing] : [existing, listener];
|
||||
existing[kEmitting] = 0;
|
||||
events[type] = existing;
|
||||
// If we've already got an array, just append.
|
||||
} else if (prepend) {
|
||||
existing.unshift(listener);
|
||||
} else {
|
||||
existing.push(listener);
|
||||
existing = ensureMutableListenerArray(events, type, existing);
|
||||
if (prepend) {
|
||||
existing.unshift(listener);
|
||||
} else {
|
||||
existing.push(listener);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for listener leak
|
||||
@@ -674,7 +682,7 @@ EventEmitter.prototype.removeListener =
|
||||
if (events === undefined)
|
||||
return this;
|
||||
|
||||
const list = events[type];
|
||||
let list = events[type];
|
||||
if (list === undefined)
|
||||
return this;
|
||||
|
||||
@@ -692,6 +700,7 @@ EventEmitter.prototype.removeListener =
|
||||
if (events.removeListener !== undefined)
|
||||
this.emit('removeListener', type, list.listener || listener);
|
||||
} else if (typeof list !== 'function') {
|
||||
list = ensureMutableListenerArray(events, type, list);
|
||||
let position = -1;
|
||||
|
||||
for (let i = list.length - 1; i >= 0; i--) {
|
||||
@@ -875,6 +884,24 @@ function arrayClone(arr) {
|
||||
return ArrayPrototypeSlice(arr);
|
||||
}
|
||||
|
||||
function cloneEventListenerArray(arr) {
|
||||
const copy = arrayClone(arr);
|
||||
copy[kEmitting] = 0;
|
||||
if (arr.warned) {
|
||||
copy.warned = true;
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
function ensureMutableListenerArray(events, type, handler) {
|
||||
if (handler[kEmitting] > 0) {
|
||||
const copy = cloneEventListenerArray(handler);
|
||||
events[type] = copy;
|
||||
return copy;
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
function unwrapListeners(arr) {
|
||||
const ret = arrayClone(arr);
|
||||
for (let i = 0; i < ret.length; ++i) {
|
||||
|
||||
@@ -78,3 +78,21 @@ assert.strictEqual(e.listeners('foo').length, 2);
|
||||
e.emit('foo');
|
||||
assert.deepStrictEqual(['callback2', 'callback3'], callbacks_called);
|
||||
assert.strictEqual(e.listeners('foo').length, 0);
|
||||
|
||||
// Verify that removing all callbacks while in emit allows the current emit to
|
||||
// propagate to all listeners.
|
||||
callbacks_called = [];
|
||||
|
||||
function callback4() {
|
||||
callbacks_called.push('callback4');
|
||||
e.removeAllListeners('foo');
|
||||
}
|
||||
|
||||
e.on('foo', callback4);
|
||||
e.on('foo', callback2);
|
||||
e.on('foo', callback3);
|
||||
assert.strictEqual(e.listeners('foo').length, 3);
|
||||
e.emit('foo');
|
||||
assert.deepStrictEqual(['callback4', 'callback2', 'callback3'],
|
||||
callbacks_called);
|
||||
assert.strictEqual(e.listeners('foo').length, 0);
|
||||
Reference in new issue
Block a user