From eb50d19461b5a0a3c893c4a26f85d825b77c962a Mon Sep 17 00:00:00 2001 From: Paolo Insogna Date: Mon, 13 Feb 2023 09:44:59 +0000 Subject: [PATCH 1/5] events: add listener argument to listenerCount --- doc/api/events.md | 11 ++++- lib/events.js | 18 ++++++- ...est-events-listener-count-with-listener.js | 47 +++++++++++++++++++ 3 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 test/parallel/test-events-listener-count-with-listener.js diff --git a/doc/api/events.md b/doc/api/events.md index 5f64fa1dec2a7a..aa4c4518008f44 100644 --- a/doc/api/events.md +++ b/doc/api/events.md @@ -646,16 +646,23 @@ Returns the current max listener value for the `EventEmitter` which is either set by [`emitter.setMaxListeners(n)`][] or defaults to [`events.defaultMaxListeners`][]. -### `emitter.listenerCount(eventName)` +### `emitter.listenerCount(eventName, [listener])` * `eventName` {string|symbol} The name of the event being listened for +* `listener` {Function} The name of the listener * Returns: {integer} Returns the number of listeners listening to the event named `eventName`. +If `listener` is provided, it will return `1` if the listener is found +in the list of the listeners of the event, `0` otherwise. ### `emitter.listeners(eventName)` @@ -2468,7 +2475,7 @@ to the `EventTarget`. [`EventTarget` error handling]: #eventtarget-error-handling [`Event` Web API]: https://dom.spec.whatwg.org/#event [`domain`]: domain.md -[`emitter.listenerCount()`]: #emitterlistenercounteventname +[`emitter.listenerCount()`]: #emitterlistenercounteventname-listener [`emitter.removeListener()`]: #emitterremovelistenereventname-listener [`emitter.setMaxListeners(n)`]: #emittersetmaxlistenersn [`event.defaultPrevented`]: #eventdefaultprevented diff --git a/lib/events.js b/lib/events.js index 4ed193dfcb9b17..4879e53d78a13b 100644 --- a/lib/events.js +++ b/lib/events.js @@ -33,6 +33,7 @@ const { ErrorCaptureStackTrace, FunctionPrototypeBind, FunctionPrototypeCall, + Number, NumberMAX_SAFE_INTEGER, ObjectDefineProperty, ObjectDefineProperties, @@ -832,17 +833,32 @@ EventEmitter.prototype.listenerCount = listenerCount; * Returns the number of listeners listening to event name * specified as `type`. * @param {string | symbol} type + * @param {Function} listener * @returns {number} */ -function listenerCount(type) { +function listenerCount(type, listener) { const events = this._events; if (events !== undefined) { const evlistener = events[type]; if (typeof evlistener === 'function') { + if (listener) { + return Number(listener === evlistener); + } + return 1; } else if (evlistener !== undefined) { + if (listener) { + for (let i = 0, l = evlistener.length; i < l; i++) { + if (evlistener[i] === listener || evlistener[i].listener === listener) { + return 1; + } + } + + return 0; + } + return evlistener.length; } } diff --git a/test/parallel/test-events-listener-count-with-listener.js b/test/parallel/test-events-listener-count-with-listener.js new file mode 100644 index 00000000000000..712cc3663a86d3 --- /dev/null +++ b/test/parallel/test-events-listener-count-with-listener.js @@ -0,0 +1,47 @@ +'use strict'; + +const common = require('../common'); +const EventEmitter = require('events'); +const assert = require('assert'); + +const EE = new EventEmitter(); +const handler = common.mustCall(undefined, 2); +const anotherHandler = common.mustCall(); + +assert.strictEqual(EE.listenerCount('event'), 0); +assert.strictEqual(EE.listenerCount('event', handler), 0); +assert.strictEqual(EE.listenerCount('event', anotherHandler), 0); + +EE.on('event', handler); + +assert.strictEqual(EE.listenerCount('event'), 1); +assert.strictEqual(EE.listenerCount('event', handler), 1); +assert.strictEqual(EE.listenerCount('event', anotherHandler), 0); + +EE.once('event', anotherHandler); + +assert.strictEqual(EE.listenerCount('event'), 2); +assert.strictEqual(EE.listenerCount('event', handler), 1); +assert.strictEqual(EE.listenerCount('event', anotherHandler), 1); + +assert.strictEqual(EE.listenerCount('another-event'), 0); +assert.strictEqual(EE.listenerCount('another-event', handler), 0); +assert.strictEqual(EE.listenerCount('another-event', anotherHandler), 0); + +EE.emit('event'); + +assert.strictEqual(EE.listenerCount('event'), 1); +assert.strictEqual(EE.listenerCount('event', handler), 1); +assert.strictEqual(EE.listenerCount('event', anotherHandler), 0); + +EE.emit('event'); + +assert.strictEqual(EE.listenerCount('event'), 1); +assert.strictEqual(EE.listenerCount('event', handler), 1); +assert.strictEqual(EE.listenerCount('event', anotherHandler), 0); + +EE.off('event', handler); + +assert.strictEqual(EE.listenerCount('event'), 0); +assert.strictEqual(EE.listenerCount('event', handler), 0); +assert.strictEqual(EE.listenerCount('event', anotherHandler), 0); From 669ffe72b0c4c9262139a129bc3cb3354e5c568a Mon Sep 17 00:00:00 2001 From: Paolo Insogna Date: Mon, 13 Feb 2023 09:47:45 +0000 Subject: [PATCH 2/5] events: update PR in docs --- doc/api/events.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/events.md b/doc/api/events.md index aa4c4518008f44..7be45dda2dfa90 100644 --- a/doc/api/events.md +++ b/doc/api/events.md @@ -652,7 +652,7 @@ set by [`emitter.setMaxListeners(n)`][] or defaults to added: v3.2.0 changes: - version: REPLACEME - pr-url: REPLACEME + pr-url: https://github.com/nodejs/node/pull/46523 description: Added the listener argument. --> From 3e116d0b726140f72c1726fc5db3764adcf6238d Mon Sep 17 00:00:00 2001 From: Paolo Insogna Date: Tue, 14 Feb 2023 09:58:27 +0100 Subject: [PATCH 3/5] events: return the number of matching listeners --- doc/api/events.md | 10 +++++----- lib/events.js | 9 +++++---- .../test-events-listener-count-with-listener.js | 8 +++++++- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/doc/api/events.md b/doc/api/events.md index 7be45dda2dfa90..533c3238a03b80 100644 --- a/doc/api/events.md +++ b/doc/api/events.md @@ -646,7 +646,7 @@ Returns the current max listener value for the `EventEmitter` which is either set by [`emitter.setMaxListeners(n)`][] or defaults to [`events.defaultMaxListeners`][]. -### `emitter.listenerCount(eventName, [listener])` +### `emitter.listenerCount(eventName[, listener])` * `eventName` {string|symbol} The name of the event being listened for -* `listener` {Function} The name of the listener +* `listener` {Function} The event handler function * Returns: {integer} -Returns the number of listeners listening to the event named `eventName`. -If `listener` is provided, it will return `1` if the listener is found -in the list of the listeners of the event, `0` otherwise. +Returns the number of listeners listening for the event named `eventName`. +If `listener` is provided, it will return how many times the listener is found +in the list of the listeners of the event. ### `emitter.listeners(eventName)` diff --git a/lib/events.js b/lib/events.js index 4879e53d78a13b..0edb76e2d916dc 100644 --- a/lib/events.js +++ b/lib/events.js @@ -33,7 +33,6 @@ const { ErrorCaptureStackTrace, FunctionPrototypeBind, FunctionPrototypeCall, - Number, NumberMAX_SAFE_INTEGER, ObjectDefineProperty, ObjectDefineProperties, @@ -844,19 +843,21 @@ function listenerCount(type, listener) { if (typeof evlistener === 'function') { if (listener) { - return Number(listener === evlistener); + return listener === evlistener ? 1 : 0; } return 1; } else if (evlistener !== undefined) { if (listener) { + let matching = 0; + for (let i = 0, l = evlistener.length; i < l; i++) { if (evlistener[i] === listener || evlistener[i].listener === listener) { - return 1; + matching++; } } - return 0; + return matching; } return evlistener.length; diff --git a/test/parallel/test-events-listener-count-with-listener.js b/test/parallel/test-events-listener-count-with-listener.js index 712cc3663a86d3..080ce6d704513e 100644 --- a/test/parallel/test-events-listener-count-with-listener.js +++ b/test/parallel/test-events-listener-count-with-listener.js @@ -5,7 +5,7 @@ const EventEmitter = require('events'); const assert = require('assert'); const EE = new EventEmitter(); -const handler = common.mustCall(undefined, 2); +const handler = common.mustCall(undefined, 3); const anotherHandler = common.mustCall(); assert.strictEqual(EE.listenerCount('event'), 0); @@ -28,6 +28,12 @@ assert.strictEqual(EE.listenerCount('another-event'), 0); assert.strictEqual(EE.listenerCount('another-event', handler), 0); assert.strictEqual(EE.listenerCount('another-event', anotherHandler), 0); +EE.once('event', handler); + +assert.strictEqual(EE.listenerCount('event'), 3); +assert.strictEqual(EE.listenerCount('event', handler), 2); +assert.strictEqual(EE.listenerCount('event', anotherHandler), 1); + EE.emit('event'); assert.strictEqual(EE.listenerCount('event'), 1); From ac6c4be2683ac7705bda0624076b2a58207d7dcd Mon Sep 17 00:00:00 2001 From: Paolo Insogna Date: Tue, 14 Feb 2023 15:27:33 +0100 Subject: [PATCH 4/5] doc: fixed documentation --- doc/api/deprecations.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api/deprecations.md b/doc/api/deprecations.md index fed6b0608473f5..13589bd2eea83c 100644 --- a/doc/api/deprecations.md +++ b/doc/api/deprecations.md @@ -752,7 +752,7 @@ changes: Type: Documentation-only The [`events.listenerCount(emitter, eventName)`][] API is -deprecated. Please use [`emitter.listenerCount(eventName)`][] instead. +deprecated. Please use [`emitter.listenerCount(eventName, listener)`][] instead. ### DEP0034: `fs.exists(path, callback)` @@ -3402,7 +3402,7 @@ be added when a function is bound to an `AsyncResource`. [`dnsPromises.lookup()`]: dns.md#dnspromiseslookuphostname-options [`domain`]: domain.md [`ecdh.setPublicKey()`]: crypto.md#ecdhsetpublickeypublickey-encoding -[`emitter.listenerCount(eventName)`]: events.md#emitterlistenercounteventname +[`emitter.listenerCount(eventName, listener)`]: events.md#emitterlistenercounteventname-listener [`events.listenerCount(emitter, eventName)`]: events.md#eventslistenercountemitter-eventname [`fs.FileHandle`]: fs.md#class-filehandle [`fs.access()`]: fs.md#fsaccesspath-mode-callback From b5af053664032ad18fe3b38fd4b22a9bd2cc5f62 Mon Sep 17 00:00:00 2001 From: Paolo Insogna Date: Mon, 20 Feb 2023 12:32:20 +0100 Subject: [PATCH 5/5] events: applied suggestions from PR review Co-authored-by: Antoine du Hamel --- doc/api/deprecations.md | 4 ++-- doc/api/events.md | 2 +- lib/events.js | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/api/deprecations.md b/doc/api/deprecations.md index 13589bd2eea83c..95f53d05768589 100644 --- a/doc/api/deprecations.md +++ b/doc/api/deprecations.md @@ -752,7 +752,7 @@ changes: Type: Documentation-only The [`events.listenerCount(emitter, eventName)`][] API is -deprecated. Please use [`emitter.listenerCount(eventName, listener)`][] instead. +deprecated. Please use [`emitter.listenerCount(eventName)`][] instead. ### DEP0034: `fs.exists(path, callback)` @@ -3402,7 +3402,7 @@ be added when a function is bound to an `AsyncResource`. [`dnsPromises.lookup()`]: dns.md#dnspromiseslookuphostname-options [`domain`]: domain.md [`ecdh.setPublicKey()`]: crypto.md#ecdhsetpublickeypublickey-encoding -[`emitter.listenerCount(eventName, listener)`]: events.md#emitterlistenercounteventname-listener +[`emitter.listenerCount(eventName)`]: events.md#emitterlistenercounteventname-listener [`events.listenerCount(emitter, eventName)`]: events.md#eventslistenercountemitter-eventname [`fs.FileHandle`]: fs.md#class-filehandle [`fs.access()`]: fs.md#fsaccesspath-mode-callback diff --git a/doc/api/events.md b/doc/api/events.md index 533c3238a03b80..0219d213a43f5f 100644 --- a/doc/api/events.md +++ b/doc/api/events.md @@ -653,7 +653,7 @@ added: v3.2.0 changes: - version: REPLACEME pr-url: https://github.com/nodejs/node/pull/46523 - description: Added the listener argument. + description: Added the `listener` argument. --> * `eventName` {string|symbol} The name of the event being listened for diff --git a/lib/events.js b/lib/events.js index 0edb76e2d916dc..75834ec106c72e 100644 --- a/lib/events.js +++ b/lib/events.js @@ -842,13 +842,13 @@ function listenerCount(type, listener) { const evlistener = events[type]; if (typeof evlistener === 'function') { - if (listener) { + if (listener != null) { return listener === evlistener ? 1 : 0; } return 1; } else if (evlistener !== undefined) { - if (listener) { + if (listener != null) { let matching = 0; for (let i = 0, l = evlistener.length; i < l; i++) {