aboutsummaryrefslogtreecommitdiff
path: root/src/js/node
diff options
context:
space:
mode:
authorGravatar Yannik Schröder <5794325+yschroe@users.noreply.github.com> 2023-10-17 01:18:40 +0200
committerGravatar GitHub <noreply@github.com> 2023-10-16 16:18:40 -0700
commit0853e19f53bfeb636971423a62214e0519b10729 (patch)
tree15af3b6bb26fb35517b5eb3248a6672744a8b25f /src/js/node
parenta9b8e3ecc82b01237721ec38afd780407c078d96 (diff)
downloadbun-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.js54
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);
}