aboutsummaryrefslogtreecommitdiff
path: root/src/js/node/events.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/js/node/events.js')
-rw-r--r--src/js/node/events.js66
1 files changed, 55 insertions, 11 deletions
diff --git a/src/js/node/events.js b/src/js/node/events.js
index bd79dd3ae..0c7434518 100644
--- a/src/js/node/events.js
+++ b/src/js/node/events.js
@@ -14,7 +14,7 @@ const ArrayPrototypeSlice = Array.prototype.slice;
var defaultMaxListeners = 10;
-// EventEmitter must be a standard function because some old code will do weird tricks like `EventEmitter.apply(this)`.
+// EventEmitter must be a standard function because some old code will do weird tricks like `EventEmitter.$apply(this)`.
const EventEmitter = function EventEmitter(opts) {
if (this._events === undefined || this._events === this.__proto__._events) {
this._events = { __proto__: null };
@@ -49,14 +49,14 @@ function emitError(emitter, args) {
if (!events) throw args[0];
var errorMonitor = events[kErrorMonitor];
if (errorMonitor) {
- for (var handler of ArrayPrototypeSlice.call(errorMonitor)) {
- handler.apply(emitter, args);
+ for (var handler of ArrayPrototypeSlice.$call(errorMonitor)) {
+ handler.$apply(emitter, args);
}
}
var handlers = events.error;
if (!handlers) throw args[0];
- for (var handler of ArrayPrototypeSlice.call(handlers)) {
- handler.apply(emitter, args);
+ for (var handler of ArrayPrototypeSlice.$call(handlers)) {
+ handler.$apply(emitter, args);
}
return true;
}
@@ -92,9 +92,30 @@ const emitWithoutRejectionCapture = function emit(type, ...args) {
if (events === undefined) return false;
var handlers = events[type];
if (handlers === undefined) return false;
-
- for (var handler of [...handlers]) {
- handler.apply(this, args);
+ // Clone handlers array if necessary since handlers can be added/removed during the loop.
+ // Cloning is skipped for performance reasons in the case of exactly one attached handler
+ // since array length changes have no side-effects in a for-loop of length 1.
+ const maybeClonedHandlers = handlers.length > 1 ? handlers.slice() : handlers;
+ for (let i = 0, { length } = maybeClonedHandlers; i < length; i++) {
+ const handler = maybeClonedHandlers[i];
+ // For performance reasons Function.call(...) is used whenever possible.
+ switch (args.length) {
+ case 0:
+ handler.$call(this);
+ break;
+ case 1:
+ handler.$call(this, args[0]);
+ break;
+ case 2:
+ handler.$call(this, args[0], args[1]);
+ break;
+ case 3:
+ handler.$call(this, args[0], args[1], args[2]);
+ break;
+ default:
+ handler.$apply(this, args);
+ break;
+ }
}
return true;
};
@@ -107,8 +128,31 @@ const emitWithRejectionCapture = function emit(type, ...args) {
if (events === undefined) return false;
var handlers = events[type];
if (handlers === undefined) return false;
- for (var handler of [...handlers]) {
- var result = handler.apply(this, args);
+ // Clone handlers array if necessary since handlers can be added/removed during the loop.
+ // Cloning is skipped for performance reasons in the case of exactly one attached handler
+ // since array length changes have no side-effects in a for-loop of length 1.
+ const maybeClonedHandlers = handlers.length > 1 ? handlers.slice() : handlers;
+ for (let i = 0, { length } = maybeClonedHandlers; i < length; i++) {
+ const handler = maybeClonedHandlers[i];
+ let result;
+ // For performance reasons Function.call(...) is used whenever possible.
+ switch (args.length) {
+ case 0:
+ result = handler.$call(this);
+ break;
+ case 1:
+ result = handler.$call(this, args[0]);
+ break;
+ case 2:
+ result = handler.$call(this, args[0], args[1]);
+ break;
+ case 3:
+ result = handler.$call(this, args[0], args[1], args[2]);
+ break;
+ default:
+ result = handler.$apply(this, args);
+ break;
+ }
if (result !== undefined && $isPromise(result)) {
addCatch(this, result, type, args);
}
@@ -181,7 +225,7 @@ function overflowWarning(emitter, type, handlers) {
function onceWrapper(type, listener, ...args) {
this.removeListener(type, listener);
- listener.apply(this, args);
+ listener.$apply(this, args);
}
EventEmitterPrototype.once = function once(type, fn) {