eventEmitterWithPromise.ts 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. import EventEmitter from "eventemitter3";
  2. import logger from "./logger";
  3. import { PeerError, PromiseEvents } from "./peerError";
  4. export class EventEmitterWithPromise<
  5. AwaitType extends EventEmitter<Events>,
  6. OpenType,
  7. ErrorType extends string,
  8. Events extends PromiseEvents<OpenType, ErrorType>,
  9. >
  10. extends EventEmitter<Events, never>
  11. implements Promise<AwaitType>
  12. {
  13. protected _open = false;
  14. readonly [Symbol.toStringTag]: string;
  15. catch<TResult = never>(
  16. onrejected?:
  17. | ((reason: PeerError<`${ErrorType}`>) => PromiseLike<TResult> | TResult)
  18. | undefined
  19. | null,
  20. ): Promise<AwaitType | TResult> {
  21. return this.then(undefined, onrejected);
  22. }
  23. finally(onfinally?: (() => void) | undefined | null): Promise<AwaitType> {
  24. return this.then().finally(onfinally);
  25. }
  26. then<TResult1 = AwaitType, TResult2 = never>(
  27. onfulfilled?:
  28. | ((value: AwaitType) => PromiseLike<TResult1> | TResult1)
  29. | undefined
  30. | null,
  31. onrejected?:
  32. | ((reason: any) => PromiseLike<TResult2> | TResult2)
  33. | undefined
  34. | null,
  35. ): Promise<TResult1 | TResult2> {
  36. const p = new Promise((resolve, reject) => {
  37. const onOpen = () => {
  38. // @ts-expect-error
  39. this.off("error", onError);
  40. // Remove 'then' to prevent potential recursion issues
  41. // `await` will wait for a Promise-like to resolve recursively
  42. resolve?.(proxyWithoutThen(this));
  43. };
  44. const onError = (err: PeerError<`${ErrorType}`>) => {
  45. // @ts-expect-error
  46. this.off("open", onOpen);
  47. reject(err);
  48. };
  49. if (this._open) {
  50. onOpen();
  51. return;
  52. }
  53. // @ts-expect-error
  54. this.once("open", onOpen);
  55. // @ts-expect-error
  56. this.once("error", onError);
  57. });
  58. return p.then(onfulfilled, onrejected);
  59. }
  60. /**
  61. * Emits a typed error message.
  62. *
  63. * @internal
  64. */
  65. emitError(type: ErrorType, err: string | Error): void {
  66. logger.error("Error:", err);
  67. // @ts-expect-error
  68. this.emit("error", new PeerError<`${ErrorType}`>(`${type}`, err));
  69. }
  70. }
  71. function proxyWithoutThen<T extends object>(obj: T): Omit<T, "then"> {
  72. return new Proxy(obj, {
  73. get(target, p, receiver) {
  74. if (p === "then") {
  75. return undefined;
  76. }
  77. return Reflect.get(target, p, receiver);
  78. },
  79. });
  80. }