Skip to content

Commit bb44f6f

Browse files
committed
events: try using map
1 parent eb29e97 commit bb44f6f

File tree

1 file changed

+79
-46
lines changed

1 file changed

+79
-46
lines changed

lib/events.js

+79-46
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ const {
8585
validateString,
8686
} = require('internal/validators');
8787

88+
const kEvents = Symbol('kEvents');
8889
const kCapture = Symbol('kCapture');
8990
const kErrorMonitor = Symbol('events.errorMonitor');
9091
const kMaxEventTargetListeners = Symbol('events.maxEventTargetListeners');
@@ -262,8 +263,7 @@ ObjectDefineProperty(EventEmitter.prototype, kCapture, {
262263
enumerable: false,
263264
});
264265

265-
EventEmitter.prototype._events = undefined;
266-
EventEmitter.prototype._eventsCount = 0;
266+
EventEmitter.prototype[kEvents] = undefined;
267267
EventEmitter.prototype._maxListeners = undefined;
268268

269269
// By default EventEmitters will print a warning if more than 10 listeners are
@@ -287,6 +287,40 @@ ObjectDefineProperty(EventEmitter, 'defaultMaxListeners', {
287287
},
288288
});
289289

290+
// _events and _eventsCount are Legacy
291+
ObjectDefineProperty(EventEmitter, '_events', {
292+
__proto__: null,
293+
enumerable: true,
294+
get: function() {
295+
// TODO - maybe in order to not break the ecosystem, create a Proxy that will update the events map
296+
const events = this[kEvents];
297+
if(events === undefined) {
298+
return undefined;
299+
}
300+
301+
return Object.fromEntries(events.entries());
302+
},
303+
set: function(events) {
304+
this[kEvents] = new Map(events);
305+
},
306+
});
307+
308+
ObjectDefineProperty(EventEmitter, '_eventsCount', {
309+
__proto__: null,
310+
enumerable: true,
311+
get: function() {
312+
const events = this[kEvents];
313+
if(events === undefined) {
314+
return 0;
315+
}
316+
317+
return events.size;
318+
},
319+
set: function() {
320+
// TODO - throw, this is not supported
321+
},
322+
});
323+
290324
ObjectDefineProperties(EventEmitter, {
291325
kMaxEventTargetListeners: {
292326
__proto__: null,
@@ -339,11 +373,9 @@ EventEmitter.setMaxListeners =
339373
// If you're updating this function definition, please also update any
340374
// re-definitions, such as the one in the Domain module (lib/domain.js).
341375
EventEmitter.init = function(opts) {
342-
343-
if (this._events === undefined ||
344-
this._events === ObjectGetPrototypeOf(this)._events) {
345-
this._events = { };
346-
this._eventsCount = 0;
376+
if (this[kEvents] === undefined ||
377+
this[kEvents] === ObjectGetPrototypeOf(this)[kEvents]) {
378+
this[kEvents] = new Map()
347379
}
348380

349381
this._maxListeners = this._maxListeners || undefined;
@@ -462,11 +494,11 @@ function enhanceStackTrace(err, own) {
462494
EventEmitter.prototype.emit = function emit(type, ...args) {
463495
let doError = (type === 'error');
464496

465-
const events = this._events;
497+
const events = this[kEvents];
466498
if (events !== undefined) {
467-
if (doError && events[kErrorMonitor] !== undefined)
499+
if (doError && events.has(kErrorMonitor))
468500
this.emit(kErrorMonitor, ...args);
469-
doError = (doError && events.error === undefined);
501+
doError = (doError && !events.has('error'));
470502
} else if (!doError)
471503
return false;
472504

@@ -506,7 +538,7 @@ EventEmitter.prototype.emit = function emit(type, ...args) {
506538
throw err; // Unhandled 'error' event
507539
}
508540

509-
const handler = events[type];
541+
const handler = events.get(type);
510542

511543
if (handler === undefined)
512544
return false;
@@ -547,10 +579,9 @@ function _addListener(target, type, listener, prepend) {
547579

548580
checkListener(listener);
549581

550-
events = target._events;
582+
events = target[kEvents];
551583
if (events === undefined) {
552-
events = target._events = { __proto__: null };
553-
target._eventsCount = 0;
584+
events = target[kEvents] = new Map();
554585
} else {
555586
// To avoid recursion in the case that type === "newListener"! Before
556587
// adding it to the listeners, first emit "newListener".
@@ -559,21 +590,20 @@ function _addListener(target, type, listener, prepend) {
559590
listener.listener ?? listener);
560591

561592
// Re-assign `events` because a newListener handler could have caused the
562-
// this._events to be assigned to a new object
563-
events = target._events;
593+
// this[kEvents] to be assigned to a new object
594+
events = target[kEvents];
564595
}
565-
existing = events[type];
596+
existing = events.get(type);
566597
}
567598

568599
if (existing === undefined) {
569600
// Optimize the case of one listener. Don't need the extra array object.
570-
events[type] = listener;
571-
++target._eventsCount;
601+
events.set(type, listener);
572602
} else {
573603
if (typeof existing === 'function') {
574604
// Adding the second element, need to change to array.
575-
existing = events[type] =
576-
prepend ? [listener, existing] : [existing, listener];
605+
existing = prepend ? [listener, existing] : [existing, listener];
606+
events.set(type, existing);
577607
// If we've already got an array, just append.
578608
} else if (prepend) {
579609
existing.unshift(listener);
@@ -677,20 +707,22 @@ EventEmitter.prototype.removeListener =
677707
function removeListener(type, listener) {
678708
checkListener(listener);
679709

680-
const events = this._events;
710+
const events = this[kEvents];
681711
if (events === undefined)
682712
return this;
683713

684-
const list = events[type];
714+
const list = events.get(type);
685715
if (list === undefined)
686716
return this;
687717

688718
if (list === listener || list.listener === listener) {
689-
if (--this._eventsCount === 0)
690-
this._events = { __proto__: null };
719+
if (this[kEvents].size === 1)
720+
// TODO - this is no longer creating new objects
721+
// this[kEvents].clear();
722+
this[kEvents] = new Map();
691723
else {
692-
delete events[type];
693-
if (events.removeListener)
724+
events.delete(type);
725+
if (events.has('removeListener'))
694726
this.emit('removeListener', type, list.listener || listener);
695727
}
696728
} else if (typeof list !== 'function') {
@@ -715,9 +747,9 @@ EventEmitter.prototype.removeListener =
715747
}
716748

717749
if (list.length === 1)
718-
events[type] = list[0];
750+
events.set(type, list[0]);
719751

720-
if (events.removeListener !== undefined)
752+
if (events.has('removeListener'))
721753
this.emit('removeListener', type, listener);
722754
}
723755

@@ -735,37 +767,38 @@ EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
735767
*/
736768
EventEmitter.prototype.removeAllListeners =
737769
function removeAllListeners(type) {
738-
const events = this._events;
770+
const events = this[kEvents];
739771
if (events === undefined)
740772
return this;
741773

742774
// Not listening for removeListener, no need to emit
743-
if (events.removeListener === undefined) {
775+
if (!events.has('removeListener')) {
744776
if (arguments.length === 0) {
745-
this._events = { __proto__: null };
746-
this._eventsCount = 0;
747-
} else if (events[type] !== undefined) {
748-
if (--this._eventsCount === 0)
749-
this._events = { __proto__: null };
777+
// this[kEvents].clear();
778+
this[kEvents]= new Map();
779+
} else if (events.has(type)) {
780+
if (this[kEvents].size === 1)
781+
// this[kEvents].clear();
782+
this[kEvents] = new Map();
750783
else
751-
delete events[type];
784+
this[kEvents].delete(type);
752785
}
753786
return this;
754787
}
755788

756789
// Emit removeListener for all listeners on all events
757790
if (arguments.length === 0) {
758-
for (const key of ReflectOwnKeys(events)) {
791+
for (const key of events.keys()) {
759792
if (key === 'removeListener') continue;
760793
this.removeAllListeners(key);
761794
}
762795
this.removeAllListeners('removeListener');
763-
this._events = { __proto__: null };
764-
this._eventsCount = 0;
796+
// this[kEvents].clear();
797+
this[kEvents] = new Map();
765798
return this;
766799
}
767800

768-
const listeners = events[type];
801+
const listeners = events.get(type);
769802

770803
if (typeof listeners === 'function') {
771804
this.removeListener(type, listeners);
@@ -780,12 +813,12 @@ EventEmitter.prototype.removeAllListeners =
780813
};
781814

782815
function _listeners(target, type, unwrap) {
783-
const events = target._events;
816+
const events = target[kEvents];
784817

785818
if (events === undefined)
786819
return [];
787820

788-
const evlistener = events[type];
821+
const evlistener = events.get(type);
789822
if (evlistener === undefined)
790823
return [];
791824

@@ -841,10 +874,10 @@ EventEmitter.prototype.listenerCount = listenerCount;
841874
* @returns {number}
842875
*/
843876
function listenerCount(type, listener) {
844-
const events = this._events;
877+
const events = this[kEvents];
845878

846879
if (events !== undefined) {
847-
const evlistener = events[type];
880+
const evlistener = events.get(type);
848881

849882
if (typeof evlistener === 'function') {
850883
if (listener != null) {
@@ -878,7 +911,7 @@ function listenerCount(type, listener) {
878911
* @returns {any[]}
879912
*/
880913
EventEmitter.prototype.eventNames = function eventNames() {
881-
return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];
914+
return this[kEvents].size > 0 ? Array.from(this[kEvents].keys()) : [];
882915
};
883916

884917
function arrayClone(arr) {

0 commit comments

Comments
 (0)