diff options
Diffstat (limited to 'src/js/node/events.js')
-rw-r--r-- | src/js/node/events.js | 66 |
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) { |