1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @flow
    
  8.  */
    
  9. 
    
  10. export default class EventEmitter<Events: Object> {
    
  11.   listenersMap: Map<string, Array<Function>> = new Map();
    
  12. 
    
  13.   addListener<Event: $Keys<Events>>(
    
  14.     event: Event,
    
  15.     listener: (...$ElementType<Events, Event>) => any,
    
  16.   ): void {
    
  17.     const listeners = this.listenersMap.get(event);
    
  18.     if (listeners === undefined) {
    
  19.       this.listenersMap.set(event, [listener]);
    
  20.     } else {
    
  21.       const index = listeners.indexOf(listener);
    
  22.       if (index < 0) {
    
  23.         listeners.push(listener);
    
  24.       }
    
  25.     }
    
  26.   }
    
  27. 
    
  28.   emit<Event: $Keys<Events>>(
    
  29.     event: Event,
    
  30.     ...args: $ElementType<Events, Event>
    
  31.   ): void {
    
  32.     const listeners = this.listenersMap.get(event);
    
  33.     if (listeners !== undefined) {
    
  34.       if (listeners.length === 1) {
    
  35.         // No need to clone or try/catch
    
  36.         const listener = listeners[0];
    
  37.         listener.apply(null, args);
    
  38.       } else {
    
  39.         let didThrow = false;
    
  40.         let caughtError = null;
    
  41. 
    
  42.         const clonedListeners = Array.from(listeners);
    
  43.         for (let i = 0; i < clonedListeners.length; i++) {
    
  44.           const listener = clonedListeners[i];
    
  45.           try {
    
  46.             listener.apply(null, args);
    
  47.           } catch (error) {
    
  48.             if (caughtError === null) {
    
  49.               didThrow = true;
    
  50.               caughtError = error;
    
  51.             }
    
  52.           }
    
  53.         }
    
  54. 
    
  55.         if (didThrow) {
    
  56.           throw caughtError;
    
  57.         }
    
  58.       }
    
  59.     }
    
  60.   }
    
  61. 
    
  62.   removeAllListeners(): void {
    
  63.     this.listenersMap.clear();
    
  64.   }
    
  65. 
    
  66.   removeListener(event: $Keys<Events>, listener: Function): void {
    
  67.     const listeners = this.listenersMap.get(event);
    
  68.     if (listeners !== undefined) {
    
  69.       const index = listeners.indexOf(listener);
    
  70.       if (index >= 0) {
    
  71.         listeners.splice(index, 1);
    
  72.       }
    
  73.     }
    
  74.   }
    
  75. }