diff options
author | 2023-10-17 01:18:40 +0200 | |
---|---|---|
committer | 2023-10-16 16:18:40 -0700 | |
commit | 0853e19f53bfeb636971423a62214e0519b10729 (patch) | |
tree | 15af3b6bb26fb35517b5eb3248a6672744a8b25f /src/js/node | |
parent | a9b8e3ecc82b01237721ec38afd780407c078d96 (diff) | |
download | bun-0853e19f53bfeb636971423a62214e0519b10729.tar.gz bun-0853e19f53bfeb636971423a62214e0519b10729.tar.zst bun-0853e19f53bfeb636971423a62214e0519b10729.zip |
perf(node:events): optimize `emit(...)` function (#5485)
Diffstat (limited to 'src/js/node')
-rw-r--r-- | src/js/node/events.js | 54 |
1 files changed, 49 insertions, 5 deletions
diff --git a/src/js/node/events.js b/src/js/node/events.js index bd79dd3ae..4af75f8e2 100644 --- a/src/js/node/events.js +++ b/src/js/node/events.js @@ -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); } |