/** * CancellablePromise extends a promise by adding the ability to flag it as cancelled. */ export class CancellablePromise { public static id = 0; public promiseId: number = 0; private promise: Promise; private isCancelled: boolean = false; private onCancel: (() => void) | null = null; private rejectPromise: (reason?: any) => void = () => {}; constructor(executor: (resolve: (value: T | PromiseLike) => void, reject: (reason?: any) => void) => void) { CancellablePromise.id += 1; this.promiseId = CancellablePromise.id; this.promise = new Promise((resolve, reject) => { // Save the reject function to use it for cancellation this.rejectPromise = reject; executor( (value) => { if (!this.isCancelled) { resolve(value); } else { reject({ cancelled: true }); } }, (reason) => { if (!this.isCancelled) { reject(reason); } else { reject({ cancelled: true }); } }, ); }); } static all(values: Array | PromiseLike>): CancellablePromise { return new CancellablePromise((resolve, reject) => { Promise.all(values.map((value) => { if (value instanceof CancellablePromise) { return value.promise; } return value; })).then(resolve, reject); }); } then( onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null, ): CancellablePromise { return new CancellablePromise((resolve, reject) => { this.promise.then( (value) => (onfulfilled ? resolve(onfulfilled(value)) : resolve(value as unknown as TResult1)), (reason) => (onrejected ? resolve(onrejected(reason)) : reject(reason)), ); }); } catch( onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null, ): CancellablePromise { return this.then(undefined, onrejected); } finally(onfinally?: (() => void) | undefined | null): CancellablePromise { return new CancellablePromise((resolve, reject) => { this.promise .then(resolve, reject) .finally(() => { if (onfinally) { onfinally(); } }); }); } public cancel() { if (!this.isCancelled) { this.isCancelled = true; if (this.onCancel) { this.onCancel(); } this.rejectPromise({ cancelled: true }); } } public onCancelled(callback: () => void) { this.onCancel = callback; } get cancelled() { return this.isCancelled; } }