GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — events: optimize once() and removeListener() · nodejs/node@73e1701

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
  • lib
    • events.js
  • test/fixtures/errors
    • events_unhandled_error_common_trace.snapshot
    • events_unhandled_error_nexttick.snapshot
    • events_unhandled_error_sameline.snapshot
    • events_unhandled_error_subclass.snapshot
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎lib/events.js‎

Copy file name to clipboardExpand all lines: lib/events.js
+74-70Lines changed: 74 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ const { addAbortListener } = require('internal/events/abort_listener');
8686

8787
const kCapture = Symbol('kCapture');
8888
const kErrorMonitor = Symbol('events.errorMonitor');
89-
const kShapeMode = Symbol('shapeMode');
9089
const kEmitting = Symbol('events.emitting');
9190
const kMaxEventTargetListeners = Symbol('events.maxEventTargetListeners');
9291
const kMaxEventTargetListenersWarned =
@@ -335,9 +334,6 @@ EventEmitter.init = function(opts) {
335334
this._events === ObjectGetPrototypeOf(this)._events) {
336335
this._events = { __proto__: null };
337336
this._eventsCount = 0;
338-
this[kShapeMode] = false;
339-
} else {
340-
this[kShapeMode] = true;
341337
}
342338

343339
this._maxListeners ||= undefined;
@@ -446,6 +442,39 @@ function enhanceStackTrace(err, own) {
446442
return err.stack + sep + ArrayPrototypeJoin(ownStack, '\n');
447443
}
448444

445+
function getUnhandledErrorException(ee, args) {
446+
let er;
447+
if (args.length > 0)
448+
er = args[0];
449+
if (er instanceof Error) {
450+
try {
451+
const capture = {};
452+
ErrorCaptureStackTrace(capture, EventEmitter.prototype.emit);
453+
ObjectDefineProperty(er, kEnhanceStackBeforeInspector, {
454+
__proto__: null,
455+
value: FunctionPrototypeBind(enhanceStackTrace, ee, er, capture),
456+
configurable: true,
457+
});
458+
} catch {
459+
// Continue regardless of error.
460+
}
461+
462+
return er;
463+
}
464+
465+
let stringifiedEr;
466+
try {
467+
stringifiedEr = inspect(er);
468+
} catch {
469+
stringifiedEr = er;
470+
}
471+
472+
// At least give some kind of context to the user
473+
const err = new ERR_UNHANDLED_ERROR(stringifiedEr);
474+
err.context = er;
475+
return err;
476+
}
477+
449478
/**
450479
* Synchronously calls each of the listeners registered
451480
* for the event.
@@ -466,38 +495,10 @@ EventEmitter.prototype.emit = function emit(type, ...args) {
466495

467496
// If there is no 'error' event listener then throw.
468497
if (doError) {
469-
let er;
470-
if (args.length > 0)
471-
er = args[0];
472-
if (er instanceof Error) {
473-
try {
474-
const capture = {};
475-
ErrorCaptureStackTrace(capture, EventEmitter.prototype.emit);
476-
ObjectDefineProperty(er, kEnhanceStackBeforeInspector, {
477-
__proto__: null,
478-
value: FunctionPrototypeBind(enhanceStackTrace, this, er, capture),
479-
configurable: true,
480-
});
481-
} catch {
482-
// Continue regardless of error.
483-
}
484-
485-
// Note: The comments on the `throw` lines are intentional, they show
486-
// up in Node's output if this results in an unhandled exception.
487-
throw er; // Unhandled 'error' event
488-
}
489-
490-
let stringifiedEr;
491-
try {
492-
stringifiedEr = inspect(er);
493-
} catch {
494-
stringifiedEr = er;
495-
}
496-
497-
// At least give some kind of context to the user
498-
const err = new ERR_UNHANDLED_ERROR(stringifiedEr);
499-
err.context = er;
500-
throw err; // Unhandled 'error' event
498+
const er = getUnhandledErrorException(this, args);
499+
// Note: The comments on the `throw` lines are intentional, they show
500+
// up in Node's output if this results in an unhandled exception.
501+
throw er; // Unhandled 'error' event
501502
}
502503

503504
const handler = events[type];
@@ -584,20 +585,23 @@ function _addListener(target, type, listener, prepend) {
584585

585586
// Check for listener leak
586587
m = _getMaxListeners(target);
587-
if (m > 0 && existing.length > m && !existing.warned) {
588-
existing.warned = true;
589-
// No error code for this since it is a Warning
590-
const w = genericNodeError(
591-
`Possible EventEmitter memory leak detected. ${existing.length} ${String(type)} listeners ` +
592-
`added to ${inspect(target, { depth: -1 })}. MaxListeners is ${m}. Use emitter.setMaxListeners() to increase limit`,
593-
{ name: 'MaxListenersExceededWarning', emitter: target, type: type, count: existing.length });
594-
process.emitWarning(w);
595-
}
588+
if (m > 0 && existing.length > m && !existing.warned)
589+
warnMaxListenersExceeded(target, type, existing, m);
596590
}
597591

598592
return target;
599593
}
600594

595+
function warnMaxListenersExceeded(target, type, existing, m) {
596+
existing.warned = true;
597+
// No error code for this since it is a Warning
598+
const w = genericNodeError(
599+
`Possible EventEmitter memory leak detected. ${existing.length} ${String(type)} listeners ` +
600+
`added to ${inspect(target, { depth: -1 })}. MaxListeners is ${m}. Use emitter.setMaxListeners() to increase limit`,
601+
{ name: 'MaxListenersExceededWarning', emitter: target, type: type, count: existing.length });
602+
process.emitWarning(w);
603+
}
604+
601605
/**
602606
* Adds a listener to the event emitter.
603607
* @param {string | symbol} type
@@ -622,22 +626,16 @@ EventEmitter.prototype.prependListener =
622626
return _addListener(this, type, listener, true);
623627
};
624628

625-
function onceWrapper() {
626-
if (!this.fired) {
627-
this.target.removeListener(this.type, this.wrapFn);
628-
this.fired = true;
629-
if (arguments.length === 0)
630-
return this.listener.call(this.target);
631-
return this.listener.apply(this.target, arguments);
632-
}
633-
}
634-
635629
function _onceWrap(target, type, listener) {
636-
const state = { fired: false, wrapFn: undefined, target, type, listener };
637-
const wrapped = onceWrapper.bind(state);
638-
wrapped.listener = listener;
639-
state.wrapFn = wrapped;
640-
return wrapped;
630+
let fired = false;
631+
function wrapper(...args) {
632+
if (fired) return;
633+
fired = true;
634+
target.removeListener(type, wrapper);
635+
return ReflectApply(listener, target, args);
636+
}
637+
wrapper.listener = listener;
638+
return wrapper;
641639
}
642640

643641
/**
@@ -689,13 +687,12 @@ EventEmitter.prototype.removeListener =
689687
if (list === listener || list.listener === listener) {
690688
this._eventsCount -= 1;
691689

692-
if (this[kShapeMode]) {
693-
events[type] = undefined;
694-
} else if (this._eventsCount === 0) {
695-
this._events = { __proto__: null };
696-
} else {
697-
delete events[type];
698-
}
690+
// Leave the key in place with an `undefined` value: repeatedly
691+
// adding and removing a listener for the same event this way keeps
692+
// the `events` object in the same shape and avoids both a `delete`
693+
// (which would put the object into dictionary mode) and allocating
694+
// a fresh object when the last listener is removed.
695+
events[type] = undefined;
699696

700697
if (events.removeListener !== undefined)
701698
this.emit('removeListener', type, list.listener || listener);
@@ -755,7 +752,6 @@ EventEmitter.prototype.removeAllListeners =
755752
else
756753
delete events[type];
757754
}
758-
this[kShapeMode] = false;
759755
return this;
760756
}
761757

@@ -768,7 +764,6 @@ EventEmitter.prototype.removeAllListeners =
768764
this.removeAllListeners('removeListener');
769765
this._events = { __proto__: null };
770766
this._eventsCount = 0;
771-
this[kShapeMode] = false;
772767
return this;
773768
}
774769

@@ -868,7 +863,16 @@ EventEmitter.prototype.listenerCount = function listenerCount(type, listener) {
868863
* @returns {(string | symbol)[]}
869864
*/
870865
EventEmitter.prototype.eventNames = function eventNames() {
871-
return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];
866+
if (this._eventsCount === 0)
867+
return [];
868+
const events = this._events;
869+
const names = [];
870+
for (const key of ReflectOwnKeys(events)) {
871+
// Removed listeners leave the key in place with an `undefined` value.
872+
if (events[key] !== undefined)
873+
ArrayPrototypePush(names, key);
874+
}
875+
return names;
872876
};
873877

874878
function arrayClone(arr) {
Collapse file

‎test/fixtures/errors/events_unhandled_error_common_trace.snapshot‎

Copy file name to clipboardExpand all lines: test/fixtures/errors/events_unhandled_error_common_trace.snapshot
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
node:events:<line>
2-
throw er; // Unhandled 'error' event
3-
^
2+
throw er; // Unhandled 'error' event
3+
^
44

55
Error: foo:bar
66
at bar (<project-root>/test/fixtures/errors/events_unhandled_error_common_trace.js:9:12)
Collapse file

‎test/fixtures/errors/events_unhandled_error_nexttick.snapshot‎

Copy file name to clipboardExpand all lines: test/fixtures/errors/events_unhandled_error_nexttick.snapshot
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
node:events:<line>
2-
throw er; // Unhandled 'error' event
3-
^
2+
throw er; // Unhandled 'error' event
3+
^
44

55
Error
66
at Object.<anonymous> (<project-root>/test/fixtures/errors/events_unhandled_error_nexttick.js:6:12)
Collapse file

‎test/fixtures/errors/events_unhandled_error_sameline.snapshot‎

Copy file name to clipboardExpand all lines: test/fixtures/errors/events_unhandled_error_sameline.snapshot
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
node:events:<line>
2-
throw er; // Unhandled 'error' event
3-
^
2+
throw er; // Unhandled 'error' event
3+
^
44

55
Error
66
at Object.<anonymous> (<project-root>/test/fixtures/errors/events_unhandled_error_sameline.js:6:34)
Collapse file

‎test/fixtures/errors/events_unhandled_error_subclass.snapshot‎

Copy file name to clipboardExpand all lines: test/fixtures/errors/events_unhandled_error_subclass.snapshot
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
node:events:<line>
2-
throw er; // Unhandled 'error' event
3-
^
2+
throw er; // Unhandled 'error' event
3+
^
44

55
Error
66
at Object.<anonymous> (<project-root>/test/fixtures/errors/events_unhandled_error_subclass.js:7:25)

0 commit comments

Comments
0 (0)
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.