Skip to content

Commit 2fe65da

Browse files
benjamingrtargos
authored andcommittedApr 24, 2021
events: getEventListeners static
PR-URL: nodejs#35991 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Matteo Collina <[email protected]>
1 parent 6ffedb9 commit 2fe65da

File tree

4 files changed

+99
-1
lines changed

4 files changed

+99
-1
lines changed
 

‎doc/api/events.md

+34
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,40 @@ class MyClass extends EventEmitter {
829829
}
830830
```
831831

832+
## `events.getEventListeners(emitterOrTarget, eventName)`
833+
<!-- YAML
834+
added:
835+
- REPLACEME
836+
-->
837+
* `emitterOrTarget` {EventEmitter|EventTarget}
838+
* `eventName` {string|symbol}
839+
* Returns: {Function[]}
840+
841+
Returns a copy of the array of listeners for the event named `eventName`.
842+
843+
For `EventEmitter`s this behaves exactly the same as calling `.listeners` on
844+
the emitter.
845+
846+
For `EventTarget`s this is the only way to get the event listeners for the
847+
event target. This is useful for debugging and diagnostic purposes.
848+
849+
```js
850+
const { getEventListeners, EventEmitter } = require('events');
851+
852+
{
853+
const ee = new EventEmitter();
854+
const listener = () => console.log('Events are fun');
855+
ee.on('foo', listener);
856+
getEventListeners(ee, 'foo'); // [listener]
857+
}
858+
{
859+
const et = new EventTarget();
860+
const listener = () => console.log('Events are fun');
861+
ee.addEventListener('foo', listener);
862+
getEventListeners(ee, 'foo'); // [listener]
863+
}
864+
```
865+
832866
## `events.once(emitter, name[, options])`
833867
<!-- YAML
834868
added:

‎lib/events.js

+25-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
'use strict';
2323

2424
const {
25+
ArrayPrototypePush,
2526
Boolean,
2627
Error,
2728
ErrorCaptureStackTrace,
@@ -74,13 +75,14 @@ const lazyDOMException = hideStackFrames((message, name) => {
7475
return new DOMException(message, name);
7576
});
7677

78+
7779
function EventEmitter(opts) {
7880
EventEmitter.init.call(this, opts);
7981
}
8082
module.exports = EventEmitter;
8183
module.exports.once = once;
8284
module.exports.on = on;
83-
85+
module.exports.getEventListeners = getEventListeners;
8486
// Backwards-compat with node 0.10.x
8587
EventEmitter.EventEmitter = EventEmitter;
8688

@@ -634,6 +636,28 @@ function unwrapListeners(arr) {
634636
return ret;
635637
}
636638

639+
function getEventListeners(emitterOrTarget, type) {
640+
// First check if EventEmitter
641+
if (typeof emitterOrTarget.listeners === 'function') {
642+
return emitterOrTarget.listeners(type);
643+
}
644+
// Require event target lazily to avoid always loading it
645+
const { isEventTarget, kEvents } = require('internal/event_target');
646+
if (isEventTarget(emitterOrTarget)) {
647+
const root = emitterOrTarget[kEvents].get(type);
648+
const listeners = [];
649+
let handler = root?.next;
650+
while (handler?.listener !== undefined) {
651+
ArrayPrototypePush(listeners, handler.listener);
652+
handler = handler.next;
653+
}
654+
return listeners;
655+
}
656+
throw new ERR_INVALID_ARG_TYPE('emitter',
657+
['EventEmitter', 'EventTarget'],
658+
emitterOrTarget);
659+
}
660+
637661
async function once(emitter, name, options = {}) {
638662
const signal = options ? options.signal : undefined;
639663
validateAbortSignal(signal, 'options.signal');

‎lib/internal/event_target.js

+2
Original file line numberDiff line numberDiff line change
@@ -635,4 +635,6 @@ module.exports = {
635635
kNewListener,
636636
kTrustEvent,
637637
kRemoveListener,
638+
kEvents,
639+
isEventTarget,
638640
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Flags: --expose-internals
2+
'use strict';
3+
4+
const common = require('../common');
5+
6+
const {
7+
deepStrictEqual,
8+
} = require('assert');
9+
10+
const { getEventListeners, EventEmitter } = require('events');
11+
const { EventTarget } = require('internal/event_target');
12+
13+
// Test getEventListeners on EventEmitter
14+
{
15+
const fn1 = common.mustNotCall();
16+
const fn2 = common.mustNotCall();
17+
const emitter = new EventEmitter();
18+
emitter.on('foo', fn1);
19+
emitter.on('foo', fn2);
20+
emitter.on('baz', fn1);
21+
emitter.on('baz', fn1);
22+
deepStrictEqual(getEventListeners(emitter, 'foo'), [fn1, fn2]);
23+
deepStrictEqual(getEventListeners(emitter, 'bar'), []);
24+
deepStrictEqual(getEventListeners(emitter, 'baz'), [fn1, fn1]);
25+
}
26+
// Test getEventListeners on EventTarget
27+
{
28+
const fn1 = common.mustNotCall();
29+
const fn2 = common.mustNotCall();
30+
const target = new EventTarget();
31+
target.addEventListener('foo', fn1);
32+
target.addEventListener('foo', fn2);
33+
target.addEventListener('baz', fn1);
34+
target.addEventListener('baz', fn1);
35+
deepStrictEqual(getEventListeners(target, 'foo'), [fn1, fn2]);
36+
deepStrictEqual(getEventListeners(target, 'bar'), []);
37+
deepStrictEqual(getEventListeners(target, 'baz'), [fn1]);
38+
}

0 commit comments

Comments
 (0)