Skip to content

Commit

Permalink
events: getEventListeners static
Browse files Browse the repository at this point in the history
PR-URL: #35991
Backport-PR-URL: #38386
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
  • Loading branch information
benjamingr authored and targos committed Apr 30, 2021
1 parent 64cd54b commit 6e734a8
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 1 deletion.
34 changes: 34 additions & 0 deletions doc/api/events.md
Expand Up @@ -829,6 +829,40 @@ class MyClass extends EventEmitter {
}
```

## `events.getEventListeners(emitterOrTarget, eventName)`
<!-- YAML
added:
- REPLACEME
-->
* `emitterOrTarget` {EventEmitter|EventTarget}
* `eventName` {string|symbol}
* Returns: {Function[]}

Returns a copy of the array of listeners for the event named `eventName`.

For `EventEmitter`s this behaves exactly the same as calling `.listeners` on
the emitter.

For `EventTarget`s this is the only way to get the event listeners for the
event target. This is useful for debugging and diagnostic purposes.

```js
const { getEventListeners, EventEmitter } = require('events');

{
const ee = new EventEmitter();
const listener = () => console.log('Events are fun');
ee.on('foo', listener);
getEventListeners(ee, 'foo'); // [listener]
}
{
const et = new EventTarget();
const listener = () => console.log('Events are fun');
ee.addEventListener('foo', listener);
getEventListeners(ee, 'foo'); // [listener]
}
```

## `events.once(emitter, name[, options])`
<!-- YAML
added:
Expand Down
26 changes: 25 additions & 1 deletion lib/events.js
Expand Up @@ -22,6 +22,7 @@
'use strict';

const {
ArrayPrototypePush,
Boolean,
Error,
ErrorCaptureStackTrace,
Expand Down Expand Up @@ -74,13 +75,14 @@ const lazyDOMException = hideStackFrames((message, name) => {
return new DOMException(message, name);
});


function EventEmitter(opts) {
EventEmitter.init.call(this, opts);
}
module.exports = EventEmitter;
module.exports.once = once;
module.exports.on = on;

module.exports.getEventListeners = getEventListeners;
// Backwards-compat with node 0.10.x
EventEmitter.EventEmitter = EventEmitter;

Expand Down Expand Up @@ -634,6 +636,28 @@ function unwrapListeners(arr) {
return ret;
}

function getEventListeners(emitterOrTarget, type) {
// First check if EventEmitter
if (typeof emitterOrTarget.listeners === 'function') {
return emitterOrTarget.listeners(type);
}
// Require event target lazily to avoid always loading it
const { isEventTarget, kEvents } = require('internal/event_target');
if (isEventTarget(emitterOrTarget)) {
const root = emitterOrTarget[kEvents].get(type);
const listeners = [];
let handler = root?.next;
while (handler?.listener !== undefined) {
ArrayPrototypePush(listeners, handler.listener);
handler = handler.next;
}
return listeners;
}
throw new ERR_INVALID_ARG_TYPE('emitter',
['EventEmitter', 'EventTarget'],
emitterOrTarget);
}

async function once(emitter, name, options = {}) {
const signal = options ? options.signal : undefined;
validateAbortSignal(signal, 'options.signal');
Expand Down
2 changes: 2 additions & 0 deletions lib/internal/event_target.js
Expand Up @@ -635,4 +635,6 @@ module.exports = {
kNewListener,
kTrustEvent,
kRemoveListener,
kEvents,
isEventTarget,
};
38 changes: 38 additions & 0 deletions test/parallel/test-events-static-geteventlisteners.js
@@ -0,0 +1,38 @@
// Flags: --expose-internals
'use strict';

const common = require('../common');

const {
deepStrictEqual,
} = require('assert');

const { getEventListeners, EventEmitter } = require('events');
const { EventTarget } = require('internal/event_target');

// Test getEventListeners on EventEmitter
{
const fn1 = common.mustNotCall();
const fn2 = common.mustNotCall();
const emitter = new EventEmitter();
emitter.on('foo', fn1);
emitter.on('foo', fn2);
emitter.on('baz', fn1);
emitter.on('baz', fn1);
deepStrictEqual(getEventListeners(emitter, 'foo'), [fn1, fn2]);
deepStrictEqual(getEventListeners(emitter, 'bar'), []);
deepStrictEqual(getEventListeners(emitter, 'baz'), [fn1, fn1]);
}
// Test getEventListeners on EventTarget
{
const fn1 = common.mustNotCall();
const fn2 = common.mustNotCall();
const target = new EventTarget();
target.addEventListener('foo', fn1);
target.addEventListener('foo', fn2);
target.addEventListener('baz', fn1);
target.addEventListener('baz', fn1);
deepStrictEqual(getEventListeners(target, 'foo'), [fn1, fn2]);
deepStrictEqual(getEventListeners(target, 'bar'), []);
deepStrictEqual(getEventListeners(target, 'baz'), [fn1]);
}

0 comments on commit 6e734a8

Please sign in to comment.