• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright Joyent, Inc. and other Node contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the
5// "Software"), to deal in the Software without restriction, including
6// without limitation the rights to use, copy, modify, merge, publish,
7// distribute, sublicense, and/or sell copies of the Software, and to permit
8// persons to whom the Software is furnished to do so, subject to the
9// following conditions:
10//
11// The above copyright notice and this permission notice shall be included
12// in all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22'use strict';
23
24const {
25  ArrayPrototypeSlice,
26  Boolean,
27  Error,
28  ErrorCaptureStackTrace,
29  MathMin,
30  NumberIsNaN,
31  ObjectCreate,
32  ObjectDefineProperty,
33  ObjectDefineProperties,
34  ObjectGetPrototypeOf,
35  ObjectSetPrototypeOf,
36  Promise,
37  PromiseReject,
38  PromiseResolve,
39  ReflectOwnKeys,
40  String,
41  Symbol,
42  SymbolFor,
43  SymbolAsyncIterator
44} = primordials;
45const kRejection = SymbolFor('nodejs.rejection');
46
47let spliceOne;
48
49const {
50  hideStackFrames,
51  kEnhanceStackBeforeInspector,
52  codes
53} = require('internal/errors');
54const {
55  ERR_INVALID_ARG_TYPE,
56  ERR_OUT_OF_RANGE,
57  ERR_UNHANDLED_ERROR
58} = codes;
59
60const {
61  inspect
62} = require('internal/util/inspect');
63
64const {
65  validateAbortSignal
66} = require('internal/validators');
67
68const kCapture = Symbol('kCapture');
69const kErrorMonitor = Symbol('events.errorMonitor');
70const kMaxEventTargetListeners = Symbol('events.maxEventTargetListeners');
71const kMaxEventTargetListenersWarned =
72  Symbol('events.maxEventTargetListenersWarned');
73
74let DOMException;
75const lazyDOMException = hideStackFrames((message, name) => {
76  if (DOMException === undefined)
77    DOMException = internalBinding('messaging').DOMException;
78  return new DOMException(message, name);
79});
80
81
82/**
83 * Creates a new `EventEmitter` instance.
84 * @param {{ captureRejections?: boolean; }} [opts]
85 * @returns {EventEmitter}
86 */
87function EventEmitter(opts) {
88  EventEmitter.init.call(this, opts);
89}
90module.exports = EventEmitter;
91module.exports.once = once;
92module.exports.on = on;
93module.exports.getEventListeners = getEventListeners;
94// Backwards-compat with node 0.10.x
95EventEmitter.EventEmitter = EventEmitter;
96
97EventEmitter.usingDomains = false;
98
99EventEmitter.captureRejectionSymbol = kRejection;
100ObjectDefineProperty(EventEmitter, 'captureRejections', {
101  get() {
102    return EventEmitter.prototype[kCapture];
103  },
104  set(value) {
105    if (typeof value !== 'boolean') {
106      throw new ERR_INVALID_ARG_TYPE('EventEmitter.captureRejections',
107                                     'boolean', value);
108    }
109
110    EventEmitter.prototype[kCapture] = value;
111  },
112  enumerable: true
113});
114
115EventEmitter.errorMonitor = kErrorMonitor;
116
117// The default for captureRejections is false
118ObjectDefineProperty(EventEmitter.prototype, kCapture, {
119  value: false,
120  writable: true,
121  enumerable: false
122});
123
124EventEmitter.prototype._events = undefined;
125EventEmitter.prototype._eventsCount = 0;
126EventEmitter.prototype._maxListeners = undefined;
127
128// By default EventEmitters will print a warning if more than 10 listeners are
129// added to it. This is a useful default which helps finding memory leaks.
130let defaultMaxListeners = 10;
131let isEventTarget;
132
133function checkListener(listener) {
134  if (typeof listener !== 'function') {
135    throw new ERR_INVALID_ARG_TYPE('listener', 'Function', listener);
136  }
137}
138
139ObjectDefineProperty(EventEmitter, 'defaultMaxListeners', {
140  enumerable: true,
141  get: function() {
142    return defaultMaxListeners;
143  },
144  set: function(arg) {
145    if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {
146      throw new ERR_OUT_OF_RANGE('defaultMaxListeners',
147                                 'a non-negative number',
148                                 arg);
149    }
150    defaultMaxListeners = arg;
151  }
152});
153
154ObjectDefineProperties(EventEmitter, {
155  kMaxEventTargetListeners: {
156    value: kMaxEventTargetListeners,
157    enumerable: false,
158    configurable: false,
159    writable: false,
160  },
161  kMaxEventTargetListenersWarned: {
162    value: kMaxEventTargetListenersWarned,
163    enumerable: false,
164    configurable: false,
165    writable: false,
166  }
167});
168
169/**
170 * Sets the max listeners.
171 * @param {number} n
172 * @param {EventTarget[] | EventEmitter[]} [eventTargets]
173 * @returns {void}
174 */
175EventEmitter.setMaxListeners =
176  function(n = defaultMaxListeners, ...eventTargets) {
177    if (typeof n !== 'number' || n < 0 || NumberIsNaN(n))
178      throw new ERR_OUT_OF_RANGE('n', 'a non-negative number', n);
179    if (eventTargets.length === 0) {
180      defaultMaxListeners = n;
181    } else {
182      if (isEventTarget === undefined)
183        isEventTarget = require('internal/event_target').isEventTarget;
184
185      for (let i = 0; i < eventTargets.length; i++) {
186        const target = eventTargets[i];
187        if (isEventTarget(target)) {
188          target[kMaxEventTargetListeners] = n;
189          target[kMaxEventTargetListenersWarned] = false;
190        } else if (typeof target.setMaxListeners === 'function') {
191          target.setMaxListeners(n);
192        } else {
193          throw new ERR_INVALID_ARG_TYPE(
194            'eventTargets',
195            ['EventEmitter', 'EventTarget'],
196            target);
197        }
198      }
199    }
200  };
201
202EventEmitter.init = function(opts) {
203
204  if (this._events === undefined ||
205      this._events === ObjectGetPrototypeOf(this)._events) {
206    this._events = ObjectCreate(null);
207    this._eventsCount = 0;
208  }
209
210  this._maxListeners = this._maxListeners || undefined;
211
212
213  if (opts?.captureRejections) {
214    if (typeof opts.captureRejections !== 'boolean') {
215      throw new ERR_INVALID_ARG_TYPE('options.captureRejections',
216                                     'boolean', opts.captureRejections);
217    }
218    this[kCapture] = Boolean(opts.captureRejections);
219  } else {
220    // Assigning the kCapture property directly saves an expensive
221    // prototype lookup in a very sensitive hot path.
222    this[kCapture] = EventEmitter.prototype[kCapture];
223  }
224};
225
226function addCatch(that, promise, type, args) {
227  if (!that[kCapture]) {
228    return;
229  }
230
231  // Handle Promises/A+ spec, then could be a getter
232  // that throws on second use.
233  try {
234    const then = promise.then;
235
236    if (typeof then === 'function') {
237      then.call(promise, undefined, function(err) {
238        // The callback is called with nextTick to avoid a follow-up
239        // rejection from this promise.
240        process.nextTick(emitUnhandledRejectionOrErr, that, err, type, args);
241      });
242    }
243  } catch (err) {
244    that.emit('error', err);
245  }
246}
247
248function emitUnhandledRejectionOrErr(ee, err, type, args) {
249  if (typeof ee[kRejection] === 'function') {
250    ee[kRejection](err, type, ...args);
251  } else {
252    // We have to disable the capture rejections mechanism, otherwise
253    // we might end up in an infinite loop.
254    const prev = ee[kCapture];
255
256    // If the error handler throws, it is not catcheable and it
257    // will end up in 'uncaughtException'. We restore the previous
258    // value of kCapture in case the uncaughtException is present
259    // and the exception is handled.
260    try {
261      ee[kCapture] = false;
262      ee.emit('error', err);
263    } finally {
264      ee[kCapture] = prev;
265    }
266  }
267}
268
269/**
270 * Increases the max listeners of the event emitter.
271 * @param {number} n
272 * @returns {EventEmitter}
273 */
274EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
275  if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {
276    throw new ERR_OUT_OF_RANGE('n', 'a non-negative number', n);
277  }
278  this._maxListeners = n;
279  return this;
280};
281
282function _getMaxListeners(that) {
283  if (that._maxListeners === undefined)
284    return EventEmitter.defaultMaxListeners;
285  return that._maxListeners;
286}
287
288/**
289 * Returns the current max listener value for the event emitter.
290 * @returns {number}
291 */
292EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
293  return _getMaxListeners(this);
294};
295
296// Returns the length and line number of the first sequence of `a` that fully
297// appears in `b` with a length of at least 4.
298function identicalSequenceRange(a, b) {
299  for (let i = 0; i < a.length - 3; i++) {
300    // Find the first entry of b that matches the current entry of a.
301    const pos = b.indexOf(a[i]);
302    if (pos !== -1) {
303      const rest = b.length - pos;
304      if (rest > 3) {
305        let len = 1;
306        const maxLen = MathMin(a.length - i, rest);
307        // Count the number of consecutive entries.
308        while (maxLen > len && a[i + len] === b[pos + len]) {
309          len++;
310        }
311        if (len > 3) {
312          return [len, i];
313        }
314      }
315    }
316  }
317
318  return [0, 0];
319}
320
321function enhanceStackTrace(err, own) {
322  let ctorInfo = '';
323  try {
324    const { name } = this.constructor;
325    if (name !== 'EventEmitter')
326      ctorInfo = ` on ${name} instance`;
327  } catch {}
328  const sep = `\nEmitted 'error' event${ctorInfo} at:\n`;
329
330  const errStack = err.stack.split('\n').slice(1);
331  const ownStack = own.stack.split('\n').slice(1);
332
333  const { 0: len, 1: off } = identicalSequenceRange(ownStack, errStack);
334  if (len > 0) {
335    ownStack.splice(off + 1, len - 2,
336                    '    [... lines matching original stack trace ...]');
337  }
338
339  return err.stack + sep + ownStack.join('\n');
340}
341
342/**
343 * Synchronously calls each of the listeners registered
344 * for the event.
345 * @param {string | symbol} type
346 * @param {...any} [args]
347 * @returns {boolean}
348 */
349EventEmitter.prototype.emit = function emit(type, ...args) {
350  let doError = (type === 'error');
351
352  const events = this._events;
353  if (events !== undefined) {
354    if (doError && events[kErrorMonitor] !== undefined)
355      this.emit(kErrorMonitor, ...args);
356    doError = (doError && events.error === undefined);
357  } else if (!doError)
358    return false;
359
360  // If there is no 'error' event listener then throw.
361  if (doError) {
362    let er;
363    if (args.length > 0)
364      er = args[0];
365    if (er instanceof Error) {
366      try {
367        const capture = {};
368        ErrorCaptureStackTrace(capture, EventEmitter.prototype.emit);
369        ObjectDefineProperty(er, kEnhanceStackBeforeInspector, {
370          value: enhanceStackTrace.bind(this, er, capture),
371          configurable: true
372        });
373      } catch {}
374
375      // Note: The comments on the `throw` lines are intentional, they show
376      // up in Node's output if this results in an unhandled exception.
377      throw er; // Unhandled 'error' event
378    }
379
380    let stringifiedEr;
381    const { inspect } = require('internal/util/inspect');
382    try {
383      stringifiedEr = inspect(er);
384    } catch {
385      stringifiedEr = er;
386    }
387
388    // At least give some kind of context to the user
389    const err = new ERR_UNHANDLED_ERROR(stringifiedEr);
390    err.context = er;
391    throw err; // Unhandled 'error' event
392  }
393
394  const handler = events[type];
395
396  if (handler === undefined)
397    return false;
398
399  if (typeof handler === 'function') {
400    const result = handler.apply(this, args);
401
402    // We check if result is undefined first because that
403    // is the most common case so we do not pay any perf
404    // penalty
405    if (result !== undefined && result !== null) {
406      addCatch(this, result, type, args);
407    }
408  } else {
409    const len = handler.length;
410    const listeners = arrayClone(handler);
411    for (let i = 0; i < len; ++i) {
412      const result = listeners[i].apply(this, args);
413
414      // We check if result is undefined first because that
415      // is the most common case so we do not pay any perf
416      // penalty.
417      // This code is duplicated because extracting it away
418      // would make it non-inlineable.
419      if (result !== undefined && result !== null) {
420        addCatch(this, result, type, args);
421      }
422    }
423  }
424
425  return true;
426};
427
428function _addListener(target, type, listener, prepend) {
429  let m;
430  let events;
431  let existing;
432
433  checkListener(listener);
434
435  events = target._events;
436  if (events === undefined) {
437    events = target._events = ObjectCreate(null);
438    target._eventsCount = 0;
439  } else {
440    // To avoid recursion in the case that type === "newListener"! Before
441    // adding it to the listeners, first emit "newListener".
442    if (events.newListener !== undefined) {
443      target.emit('newListener', type,
444                  listener.listener ? listener.listener : listener);
445
446      // Re-assign `events` because a newListener handler could have caused the
447      // this._events to be assigned to a new object
448      events = target._events;
449    }
450    existing = events[type];
451  }
452
453  if (existing === undefined) {
454    // Optimize the case of one listener. Don't need the extra array object.
455    events[type] = listener;
456    ++target._eventsCount;
457  } else {
458    if (typeof existing === 'function') {
459      // Adding the second element, need to change to array.
460      existing = events[type] =
461        prepend ? [listener, existing] : [existing, listener];
462      // If we've already got an array, just append.
463    } else if (prepend) {
464      existing.unshift(listener);
465    } else {
466      existing.push(listener);
467    }
468
469    // Check for listener leak
470    m = _getMaxListeners(target);
471    if (m > 0 && existing.length > m && !existing.warned) {
472      existing.warned = true;
473      // No error code for this since it is a Warning
474      // eslint-disable-next-line no-restricted-syntax
475      const w = new Error('Possible EventEmitter memory leak detected. ' +
476                          `${existing.length} ${String(type)} listeners ` +
477                          `added to ${inspect(target, { depth: -1 })}. Use ` +
478                          'emitter.setMaxListeners() to increase limit');
479      w.name = 'MaxListenersExceededWarning';
480      w.emitter = target;
481      w.type = type;
482      w.count = existing.length;
483      process.emitWarning(w);
484    }
485  }
486
487  return target;
488}
489
490/**
491 * Adds a listener to the event emitter.
492 * @param {string | symbol} type
493 * @param {Function} listener
494 * @returns {EventEmitter}
495 */
496EventEmitter.prototype.addListener = function addListener(type, listener) {
497  return _addListener(this, type, listener, false);
498};
499
500EventEmitter.prototype.on = EventEmitter.prototype.addListener;
501
502/**
503 * Adds the `listener` function to the beginning of
504 * the listeners array.
505 * @param {string | symbol} type
506 * @param {Function} listener
507 * @returns {EventEmitter}
508 */
509EventEmitter.prototype.prependListener =
510    function prependListener(type, listener) {
511      return _addListener(this, type, listener, true);
512    };
513
514function onceWrapper() {
515  if (!this.fired) {
516    this.target.removeListener(this.type, this.wrapFn);
517    this.fired = true;
518    if (arguments.length === 0)
519      return this.listener.call(this.target);
520    return this.listener.apply(this.target, arguments);
521  }
522}
523
524function _onceWrap(target, type, listener) {
525  const state = { fired: false, wrapFn: undefined, target, type, listener };
526  const wrapped = onceWrapper.bind(state);
527  wrapped.listener = listener;
528  state.wrapFn = wrapped;
529  return wrapped;
530}
531
532/**
533 * Adds a one-time `listener` function to the event emitter.
534 * @param {string | symbol} type
535 * @param {Function} listener
536 * @returns {EventEmitter}
537 */
538EventEmitter.prototype.once = function once(type, listener) {
539  checkListener(listener);
540
541  this.on(type, _onceWrap(this, type, listener));
542  return this;
543};
544
545/**
546 * Adds a one-time `listener` function to the beginning of
547 * the listeners array.
548 * @param {string | symbol} type
549 * @param {Function} listener
550 * @returns {EventEmitter}
551 */
552EventEmitter.prototype.prependOnceListener =
553    function prependOnceListener(type, listener) {
554      checkListener(listener);
555
556      this.prependListener(type, _onceWrap(this, type, listener));
557      return this;
558    };
559
560/**
561 * Removes the specified `listener` from the listeners array.
562 * @param {string | symbol} type
563 * @param {Function} listener
564 * @returns {EventEmitter}
565 */
566EventEmitter.prototype.removeListener =
567    function removeListener(type, listener) {
568      checkListener(listener);
569
570      const events = this._events;
571      if (events === undefined)
572        return this;
573
574      const list = events[type];
575      if (list === undefined)
576        return this;
577
578      if (list === listener || list.listener === listener) {
579        if (--this._eventsCount === 0)
580          this._events = ObjectCreate(null);
581        else {
582          delete events[type];
583          if (events.removeListener)
584            this.emit('removeListener', type, list.listener || listener);
585        }
586      } else if (typeof list !== 'function') {
587        let position = -1;
588
589        for (let i = list.length - 1; i >= 0; i--) {
590          if (list[i] === listener || list[i].listener === listener) {
591            position = i;
592            break;
593          }
594        }
595
596        if (position < 0)
597          return this;
598
599        if (position === 0)
600          list.shift();
601        else {
602          if (spliceOne === undefined)
603            spliceOne = require('internal/util').spliceOne;
604          spliceOne(list, position);
605        }
606
607        if (list.length === 1)
608          events[type] = list[0];
609
610        if (events.removeListener !== undefined)
611          this.emit('removeListener', type, listener);
612      }
613
614      return this;
615    };
616
617EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
618
619/**
620 * Removes all listeners from the event emitter. (Only
621 * removes listeners for a specific event name if specified
622 * as `type`).
623 * @param {string | symbol} [type]
624 * @returns {EventEmitter}
625 */
626EventEmitter.prototype.removeAllListeners =
627    function removeAllListeners(type) {
628      const events = this._events;
629      if (events === undefined)
630        return this;
631
632      // Not listening for removeListener, no need to emit
633      if (events.removeListener === undefined) {
634        if (arguments.length === 0) {
635          this._events = ObjectCreate(null);
636          this._eventsCount = 0;
637        } else if (events[type] !== undefined) {
638          if (--this._eventsCount === 0)
639            this._events = ObjectCreate(null);
640          else
641            delete events[type];
642        }
643        return this;
644      }
645
646      // Emit removeListener for all listeners on all events
647      if (arguments.length === 0) {
648        for (const key of ReflectOwnKeys(events)) {
649          if (key === 'removeListener') continue;
650          this.removeAllListeners(key);
651        }
652        this.removeAllListeners('removeListener');
653        this._events = ObjectCreate(null);
654        this._eventsCount = 0;
655        return this;
656      }
657
658      const listeners = events[type];
659
660      if (typeof listeners === 'function') {
661        this.removeListener(type, listeners);
662      } else if (listeners !== undefined) {
663        // LIFO order
664        for (let i = listeners.length - 1; i >= 0; i--) {
665          this.removeListener(type, listeners[i]);
666        }
667      }
668
669      return this;
670    };
671
672function _listeners(target, type, unwrap) {
673  const events = target._events;
674
675  if (events === undefined)
676    return [];
677
678  const evlistener = events[type];
679  if (evlistener === undefined)
680    return [];
681
682  if (typeof evlistener === 'function')
683    return unwrap ? [evlistener.listener || evlistener] : [evlistener];
684
685  return unwrap ?
686    unwrapListeners(evlistener) : arrayClone(evlistener);
687}
688
689/**
690 * Returns a copy of the array of listeners for the event name
691 * specified as `type`.
692 * @param {string | symbol} type
693 * @returns {Function[]}
694 */
695EventEmitter.prototype.listeners = function listeners(type) {
696  return _listeners(this, type, true);
697};
698
699/**
700 * Returns a copy of the array of listeners and wrappers for
701 * the event name specified as `type`.
702 * @param {string | symbol} type
703 * @returns {Function[]}
704 */
705EventEmitter.prototype.rawListeners = function rawListeners(type) {
706  return _listeners(this, type, false);
707};
708
709/**
710 * Returns the number of listeners listening to the event name
711 * specified as `type`.
712 * @deprecated since v3.2.0
713 * @param {EventEmitter} emitter
714 * @param {string | symbol} type
715 * @returns {number}
716 */
717EventEmitter.listenerCount = function(emitter, type) {
718  if (typeof emitter.listenerCount === 'function') {
719    return emitter.listenerCount(type);
720  }
721  return listenerCount.call(emitter, type);
722};
723
724EventEmitter.prototype.listenerCount = listenerCount;
725
726/**
727 * Returns the number of listeners listening to event name
728 * specified as `type`.
729 * @param {string | symbol} type
730 * @returns {number}
731 */
732function listenerCount(type) {
733  const events = this._events;
734
735  if (events !== undefined) {
736    const evlistener = events[type];
737
738    if (typeof evlistener === 'function') {
739      return 1;
740    } else if (evlistener !== undefined) {
741      return evlistener.length;
742    }
743  }
744
745  return 0;
746}
747
748/**
749 * Returns an array listing the events for which
750 * the emitter has registered listeners.
751 * @returns {any[]}
752 */
753EventEmitter.prototype.eventNames = function eventNames() {
754  return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];
755};
756
757function arrayClone(arr) {
758  // At least since V8 8.3, this implementation is faster than the previous
759  // which always used a simple for-loop
760  switch (arr.length) {
761    case 2: return [arr[0], arr[1]];
762    case 3: return [arr[0], arr[1], arr[2]];
763    case 4: return [arr[0], arr[1], arr[2], arr[3]];
764    case 5: return [arr[0], arr[1], arr[2], arr[3], arr[4]];
765    case 6: return [arr[0], arr[1], arr[2], arr[3], arr[4], arr[5]];
766  }
767  return ArrayPrototypeSlice(arr);
768}
769
770function unwrapListeners(arr) {
771  const ret = arrayClone(arr);
772  for (let i = 0; i < ret.length; ++i) {
773    const orig = ret[i].listener;
774    if (typeof orig === 'function')
775      ret[i] = orig;
776  }
777  return ret;
778}
779
780/**
781 * Returns a copy of the array of listeners for the event name
782 * specified as `type`.
783 * @param {EventEmitter | EventTarget} emitterOrTarget
784 * @param {string | symbol} type
785 * @returns {Function[]}
786 */
787function getEventListeners(emitterOrTarget, type) {
788  // First check if EventEmitter
789  if (typeof emitterOrTarget.listeners === 'function') {
790    return emitterOrTarget.listeners(type);
791  }
792  // Require event target lazily to avoid always loading it
793  const { isEventTarget, kEvents } = require('internal/event_target');
794  if (isEventTarget(emitterOrTarget)) {
795    const root = emitterOrTarget[kEvents].get(type);
796    const listeners = [];
797    let handler = root?.next;
798    while (handler?.listener !== undefined) {
799      listeners.push(handler.listener);
800      handler = handler.next;
801    }
802    return listeners;
803  }
804  throw new ERR_INVALID_ARG_TYPE('emitter',
805                                 ['EventEmitter', 'EventTarget'],
806                                 emitterOrTarget);
807}
808
809/**
810 * Creates a `Promise` that is fulfilled when the emitter
811 * emits the given event.
812 * @param {EventEmitter} emitter
813 * @param {string} name
814 * @param {{ signal: AbortSignal; }} [options]
815 * @returns {Promise}
816 */
817async function once(emitter, name, options = {}) {
818  const signal = options?.signal;
819  validateAbortSignal(signal, 'options.signal');
820  if (signal?.aborted)
821    throw lazyDOMException('The operation was aborted', 'AbortError');
822  return new Promise((resolve, reject) => {
823    const errorListener = (err) => {
824      emitter.removeListener(name, resolver);
825      if (signal != null) {
826        eventTargetAgnosticRemoveListener(
827          signal,
828          'abort',
829          abortListener,
830          { once: true });
831      }
832      reject(err);
833    };
834    const resolver = (...args) => {
835      if (typeof emitter.removeListener === 'function') {
836        emitter.removeListener('error', errorListener);
837      }
838      if (signal != null) {
839        eventTargetAgnosticRemoveListener(
840          signal,
841          'abort',
842          abortListener,
843          { once: true });
844      }
845      resolve(args);
846    };
847    eventTargetAgnosticAddListener(emitter, name, resolver, { once: true });
848    if (name !== 'error') {
849      addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true });
850    }
851    function abortListener() {
852      if (typeof emitter.removeListener === 'function') {
853        emitter.removeListener(name, resolver);
854        emitter.removeListener('error', errorListener);
855      } else {
856        eventTargetAgnosticRemoveListener(
857          emitter,
858          name,
859          resolver,
860          { once: true });
861        eventTargetAgnosticRemoveListener(
862          emitter,
863          'error',
864          errorListener,
865          { once: true });
866      }
867      reject(lazyDOMException('The operation was aborted', 'AbortError'));
868    }
869    if (signal != null) {
870      signal.addEventListener('abort', abortListener, { once: true });
871    }
872  });
873}
874
875const AsyncIteratorPrototype = ObjectGetPrototypeOf(
876  ObjectGetPrototypeOf(async function* () {}).prototype);
877
878function createIterResult(value, done) {
879  return { value, done };
880}
881
882function addErrorHandlerIfEventEmitter(emitter, handler, flags) {
883  if (typeof emitter.on === 'function') {
884    eventTargetAgnosticAddListener(emitter, 'error', handler, flags);
885  }
886}
887
888function eventTargetAgnosticRemoveListener(emitter, name, listener, flags) {
889  if (typeof emitter.removeListener === 'function') {
890    emitter.removeListener(name, listener);
891  } else if (typeof emitter.removeEventListener === 'function') {
892    emitter.removeEventListener(name, listener, flags);
893  } else {
894    throw new ERR_INVALID_ARG_TYPE('emitter', 'EventEmitter', emitter);
895  }
896}
897
898function eventTargetAgnosticAddListener(emitter, name, listener, flags) {
899  if (typeof emitter.on === 'function') {
900    if (flags?.once) {
901      emitter.once(name, listener);
902    } else {
903      emitter.on(name, listener);
904    }
905  } else if (typeof emitter.addEventListener === 'function') {
906    // EventTarget does not have `error` event semantics like Node
907    // EventEmitters, we do not listen to `error` events here.
908    emitter.addEventListener(name, (arg) => { listener(arg); }, flags);
909  } else {
910    throw new ERR_INVALID_ARG_TYPE('emitter', 'EventEmitter', emitter);
911  }
912}
913
914/**
915 * Returns an `AsyncIterator` that iterates `event` events.
916 * @param {EventEmitter} emitter
917 * @param {string | symbol} event
918 * @param {{ signal: AbortSignal; }} [options]
919 * @returns {AsyncIterator}
920 */
921function on(emitter, event, options) {
922  const signal = options?.signal;
923  validateAbortSignal(signal, 'options.signal');
924  if (signal?.aborted) {
925    throw lazyDOMException('The operation was aborted', 'AbortError');
926  }
927
928  const unconsumedEvents = [];
929  const unconsumedPromises = [];
930  let error = null;
931  let finished = false;
932
933  const iterator = ObjectSetPrototypeOf({
934    next() {
935      // First, we consume all unread events
936      const value = unconsumedEvents.shift();
937      if (value) {
938        return PromiseResolve(createIterResult(value, false));
939      }
940
941      // Then we error, if an error happened
942      // This happens one time if at all, because after 'error'
943      // we stop listening
944      if (error) {
945        const p = PromiseReject(error);
946        // Only the first element errors
947        error = null;
948        return p;
949      }
950
951      // If the iterator is finished, resolve to done
952      if (finished) {
953        return PromiseResolve(createIterResult(undefined, true));
954      }
955
956      // Wait until an event happens
957      return new Promise(function(resolve, reject) {
958        unconsumedPromises.push({ resolve, reject });
959      });
960    },
961
962    return() {
963      eventTargetAgnosticRemoveListener(emitter, event, eventHandler);
964      eventTargetAgnosticRemoveListener(emitter, 'error', errorHandler);
965
966      if (signal) {
967        eventTargetAgnosticRemoveListener(
968          signal,
969          'abort',
970          abortListener,
971          { once: true });
972      }
973
974      finished = true;
975
976      for (const promise of unconsumedPromises) {
977        promise.resolve(createIterResult(undefined, true));
978      }
979
980      return PromiseResolve(createIterResult(undefined, true));
981    },
982
983    throw(err) {
984      if (!err || !(err instanceof Error)) {
985        throw new ERR_INVALID_ARG_TYPE('EventEmitter.AsyncIterator',
986                                       'Error', err);
987      }
988      error = err;
989      eventTargetAgnosticRemoveListener(emitter, event, eventHandler);
990      eventTargetAgnosticRemoveListener(emitter, 'error', errorHandler);
991    },
992
993    [SymbolAsyncIterator]() {
994      return this;
995    }
996  }, AsyncIteratorPrototype);
997
998  eventTargetAgnosticAddListener(emitter, event, eventHandler);
999  if (event !== 'error') {
1000    addErrorHandlerIfEventEmitter(emitter, errorHandler);
1001  }
1002
1003  if (signal) {
1004    eventTargetAgnosticAddListener(
1005      signal,
1006      'abort',
1007      abortListener,
1008      { once: true });
1009  }
1010
1011  return iterator;
1012
1013  function abortListener() {
1014    errorHandler(lazyDOMException('The operation was aborted', 'AbortError'));
1015  }
1016
1017  function eventHandler(...args) {
1018    const promise = unconsumedPromises.shift();
1019    if (promise) {
1020      promise.resolve(createIterResult(args, false));
1021    } else {
1022      unconsumedEvents.push(args);
1023    }
1024  }
1025
1026  function errorHandler(err) {
1027    finished = true;
1028
1029    const toError = unconsumedPromises.shift();
1030
1031    if (toError) {
1032      toError.reject(err);
1033    } else {
1034      // The next time we call next()
1035      error = err;
1036    }
1037
1038    iterator.return();
1039  }
1040}
1041