Skip to content

Commit 3a0bcfe

Browse files
committed
fix EventTarget support
Port of nodejs/node#33659
1 parent a205441 commit 3a0bcfe

File tree

2 files changed

+80
-35
lines changed

2 files changed

+80
-35
lines changed

events.js

Lines changed: 49 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,49 @@ function createIterResult(value, done) {
472472
return { value: value, done: done };
473473
}
474474

475+
function addErrorHandlerIfEventEmitter(emitter, handler, flags) {
476+
if (typeof emitter.on === 'function') {
477+
eventTargetAgnosticAddListener(emitter, 'error', handler, flags);
478+
}
479+
}
480+
481+
function eventTargetAgnosticRemoveListener(emitter, name, listener, flags) {
482+
if (typeof emitter.removeListener === 'function') {
483+
emitter.removeListener(name, listener);
484+
} else if (typeof emitter.removeEventListener === 'function') {
485+
emitter.removeEventListener(name, listener, flags);
486+
} else {
487+
throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter);
488+
}
489+
}
490+
491+
function eventTargetAgnosticAddListener(emitter, name, listener, flags) {
492+
if (typeof emitter.on === 'function') {
493+
if (flags && flags.once) {
494+
emitter.once(name, listener);
495+
} else {
496+
emitter.on(name, listener);
497+
}
498+
} else if (typeof emitter.addEventListener === 'function') {
499+
// EventTarget does not have `error` event semantics like Node
500+
// EventEmitters, we do not listen for `error` events here.
501+
// TODO this wrapListener should not happen here: it means users cannot do
502+
// `emitter.removeEventListener(listener)`.
503+
// Might be impossible to work around; if so we will have to rely on builtin
504+
// `{ once: true }` support after all
505+
emitter.addEventListener(name, function wrapListener(arg) {
506+
// IE does not have builtin `{ once: true }` support so we
507+
// have to do it manually.
508+
if (flags && flags.once) {
509+
emitter.removeEventListener(name, wrapListener);
510+
}
511+
listener(arg);
512+
});
513+
} else {
514+
throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter);
515+
}
516+
}
517+
475518
var AsyncIteratorPrototype = undefined;
476519

477520
function on(emitter, event) {
@@ -519,8 +562,8 @@ function on(emitter, event) {
519562
});
520563
},
521564
'return': function _return() {
522-
emitter.removeListener(event, eventHandler);
523-
emitter.removeListener('error', errorHandler);
565+
eventTargetAgnosticRemoveListener(emitter, event, eventHandler);
566+
eventTargetAgnosticRemoveListener(emitter, 'error', errorHandler);
524567
finished = true;
525568

526569
for (var i = 0, l = unconsumedPromises.length; i < l; i++) {
@@ -533,17 +576,17 @@ function on(emitter, event) {
533576
throw new TypeError('The "EventEmitter.AsyncIterator" property must be an instance of Error. Received ' + typeof err);
534577
}
535578
error = err;
536-
emitter.removeListener(event, eventHandler);
537-
emitter.removeListener('error', errorHandler);
579+
eventTargetAgnosticRemoveListener(emitter, event, eventHandler);
580+
eventTargetAgnosticRemoveListener(emitter, 'error', errorHandler);
538581
}
539582
};
540583

541584
iterator[Symbol.asyncIterator] = function () { return this; };
542585

543586
Object.setPrototypeOf(iterator, AsyncIteratorPrototype);
544587

545-
emitter.on(event, eventHandler);
546-
emitter.on('error', errorHandler);
588+
eventTargetAgnosticAddListener(emitter, event, eventHandler);
589+
eventTargetAgnosticAddListener(emitter, 'error', errorHandler);
547590

548591
return iterator;
549592

@@ -570,32 +613,3 @@ function on(emitter, event) {
570613
iterator.return();
571614
}
572615
}
573-
574-
function addErrorHandlerIfEventEmitter(emitter, handler, flags) {
575-
if (typeof emitter.on === 'function') {
576-
eventTargetAgnosticAddListener(emitter, 'error', handler, flags);
577-
}
578-
}
579-
580-
function eventTargetAgnosticAddListener(emitter, name, listener, flags) {
581-
if (typeof emitter.on === 'function') {
582-
if (flags.once) {
583-
emitter.once(name, listener);
584-
} else {
585-
emitter.on(name, listener);
586-
}
587-
} else if (typeof emitter.addEventListener === 'function') {
588-
// EventTarget does not have `error` event semantics like Node
589-
// EventEmitters, we do not listen for `error` events here.
590-
emitter.addEventListener(name, function wrapListener(arg) {
591-
// IE does not have builtin `{ once: true }` support so we
592-
// have to do it manually.
593-
if (flags.once) {
594-
emitter.removeEventListener(name, wrapListener);
595-
}
596-
listener(arg);
597-
});
598-
} else {
599-
throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter);
600-
}
601-
}

tests/on-async-iterator.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,28 @@ async function iterableThrow() {
205205
assert.strictEqual(ee.listenerCount('error'), 0);
206206
}
207207

208+
async function eventTarget() {
209+
const et = new EventTarget();
210+
const tick = () => et.dispatchEvent(new Event('tick'));
211+
const interval = setInterval(tick, 0);
212+
let count = 0;
213+
for await (const [ event ] of on(et, 'tick')) {
214+
count++;
215+
assert.strictEqual(event.type, 'tick');
216+
if (count >= 5) {
217+
break;
218+
}
219+
}
220+
assert.strictEqual(count, 5);
221+
clearInterval(interval);
222+
}
223+
224+
async function errorListenerCount() {
225+
const et = new EventEmitter();
226+
on(et, 'foo');
227+
assert.strictEqual(et.listenerCount('error'), 1);
228+
}
229+
208230
async function run() {
209231
var funcs = [
210232
basic,
@@ -216,6 +238,15 @@ async function run() {
216238
iterableThrow,
217239
];
218240

241+
if (typeof EventTarget === 'function') {
242+
funcs.push(
243+
eventTarget,
244+
errorListenerCount
245+
);
246+
} else {
247+
common.test.comment('Skipping EventTarget tests');
248+
}
249+
219250
for (var fn of funcs) {
220251
await fn();
221252
}

0 commit comments

Comments
 (0)