import * as Ex from "./AsyncExit"; import type { InterruptionState } from "./InterruptionState"; export class CancellablePromise { readonly _E!: () => E; private reject: ((e: Ex.Rejection) => void) | undefined = undefined; private current: Promise | undefined = undefined; constructor( readonly factory: (onInterrupt: (f: () => void) => void) => Promise, readonly interruptionState: InterruptionState ) {} promise(): Promise { if (this.current) { throw new Error("Bug: promise() has been called twice"); } if (this.interruptionState.interrupted) { throw new Error("Bug: promise already interrupted on creation"); } const onInterrupt: Array<() => void> = []; const removeListener = this.interruptionState.listen(() => { onInterrupt.forEach((f) => { f(); }); this.interrupt(); }); const p = new Promise((resolve, reject) => { this.reject = reject; this.factory((f) => { onInterrupt.push(f); }) .then((a) => { removeListener(); if (!this.interruptionState.interrupted) { resolve(a); } }) .catch((e) => { removeListener(); if (!this.interruptionState.interrupted) { reject(e); } }); }); this.current = p; return p; } interrupt() { this.reject?.(Ex.interrupted()); } }