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