diff --git a/benchmark/events/ee-event-names.js b/benchmark/events/ee-event-names.js new file mode 100644 index 000000000000..9b2ca4ac487f --- /dev/null +++ b/benchmark/events/ee-event-names.js @@ -0,0 +1,55 @@ +'use strict'; + +const common = require('../common'); +const assert = require('node:assert'); +const { EventEmitter } = require('node:events'); + +const bench = common.createBenchmark(main, { + n: [1e6], + size: [0, 4, 32], + shape: ['strings', 'mixed', 'removed'], + mode: ['read', 'churn'], +}); + +function main({ n, size, shape, mode }) { + const emitter = new EventEmitter(); + const listener = () => {}; + const keys = []; + for (let index = 0; index < size; index++) { + const key = shape === 'mixed' && index % 2 === 0 ? + Symbol(`event${index}`) : `event${index}`; + emitter.on(key, listener); + if (shape === 'removed' && index % 2 === 0) { + emitter.removeListener(key, listener); + } else { + keys.push(key); + } + } + if (mode === 'churn') keys.push('transient'); + const expected = [ + ...keys.filter((key) => typeof key === 'string'), + ...keys.filter((key) => typeof key === 'symbol'), + ]; + const last = expected[expected.length - 1]; + let checksum = 0; + let result; + bench.start(); + if (mode === 'read') { + for (let iteration = 0; iteration < n; iteration++) { + result = emitter.eventNames(); + checksum += result.length; + if (result.length !== 0 && result[result.length - 1] === last) checksum++; + } + } else { + for (let iteration = 0; iteration < n; iteration++) { + emitter.on('transient', listener); + result = emitter.eventNames(); + checksum += result.length; + if (result.length !== 0 && result[result.length - 1] === last) checksum++; + emitter.removeListener('transient', listener); + } + } + bench.end(n); + assert.deepStrictEqual(result, expected); + assert.strictEqual(checksum, n * (expected.length + (expected.length !== 0))); +} diff --git a/lib/events.js b/lib/events.js index 8b20ea994d6c..b10a0863175f 100644 --- a/lib/events.js +++ b/lib/events.js @@ -877,7 +877,7 @@ EventEmitter.prototype.eventNames = function eventNames() { for (const key of ReflectOwnKeys(events)) { // Removed listeners leave the key in place with an `undefined` value. if (events[key] !== undefined) - ArrayPrototypePush(names, key); + names[names.length] = key; } return names; }; diff --git a/test/parallel/test-event-emitter-event-names-results.js b/test/parallel/test-event-emitter-event-names-results.js new file mode 100644 index 000000000000..472bf910ffd1 --- /dev/null +++ b/test/parallel/test-event-emitter-event-names-results.js @@ -0,0 +1,26 @@ +'use strict'; + +require('../common'); +const assert = require('node:assert'); +const { EventEmitter } = require('node:events'); + +const emitter = new EventEmitter(); +const listener = () => {}; +const symbol = Symbol('event'); +const keys = ['third', symbol, '2', '__proto__', 'first', '1']; +for (const key of keys) emitter.on(key, listener); + +const expected = ['1', '2', 'third', '__proto__', 'first', symbol]; +const result = emitter.eventNames(); +assert.deepStrictEqual(result, expected); +result[0] = 'changed'; +result.push('extra'); +assert.deepStrictEqual(emitter.eventNames(), expected); + +emitter.removeListener('2', listener); +emitter.removeListener('third', listener); +assert.deepStrictEqual(emitter.eventNames(), ['1', '__proto__', 'first', symbol]); +emitter.on('third', listener); +assert.deepStrictEqual(emitter.eventNames(), ['1', '__proto__', 'first', 'third', symbol]); +emitter.removeAllListeners(); +assert.deepStrictEqual(emitter.eventNames(), []);