22
33export class CancelError extends Error {
44
5- constructor(reason : string = 'Promise was canceled' ) {
6- super(reason );
5+ constructor(message : string) {
6+ super(message );
77 this.name = 'CancelError';
88 }
99
@@ -13,7 +13,8 @@ export class CancelError extends Error {
1313}
1414
1515export interface OnCancel {
16- readonly isPending: boolean;
16+ readonly isResolved: boolean;
17+ readonly isRejected: boolean;
1718 readonly isCancelled: boolean;
1819
1920 (cancelHandler: () => void): void;
@@ -22,7 +23,8 @@ export interface OnCancel {
2223export class CancelablePromise<T > implements Promise<T > {
2324 readonly [Symbol.toStringTag]: string;
2425
25- #isPending: boolean;
26+ #isResolved: boolean;
27+ #isRejected: boolean;
2628 #isCancelled: boolean;
2729 readonly #cancelHandlers: (() => void)[];
2830 readonly #promise: Promise<T >;
@@ -36,33 +38,43 @@ export class CancelablePromise<T> implements Promise<T> {
3638 onCancel: OnCancel
3739 ) => void
3840 ) {
39- this.#isPending = true;
41+ this.#isResolved = false;
42+ this.#isRejected = false;
4043 this.#isCancelled = false;
4144 this.#cancelHandlers = [];
4245 this.#promise = new Promise<T >((resolve, reject) => {
4346 this.#resolve = resolve;
4447 this.#reject = reject;
4548
4649 const onResolve = (value: T | PromiseLike<T >): void => {
47- if (!this.#isCancelled) {
48- this.#isPending = false;
49- this.#resolve?.(value);
50+ if (this.#isResolved || this.#isRejected || this.#isCancelled) {
51+ return;
5052 }
53+ this.#isResolved = true;
54+ this.#resolve?.(value);
5155 };
5256
5357 const onReject = (reason?: any): void => {
54- this.#isPending = false;
58+ if (this.#isResolved || this.#isRejected || this.#isCancelled) {
59+ return;
60+ }
61+ this.#isRejected = true;
5562 this.#reject?.(reason);
5663 };
5764
5865 const onCancel = (cancelHandler: () => void): void => {
59- if (this.#isPending ) {
60- this.#cancelHandlers.push(cancelHandler) ;
66+ if (this.#isResolved || this.#isRejected || this.#isCancelled ) {
67+ return ;
6168 }
69+ this.#cancelHandlers.push(cancelHandler);
6270 };
6371
64- Object.defineProperty(onCancel, 'isPending', {
65- get: (): boolean => this.#isPending,
72+ Object.defineProperty(onCancel, 'isResolved', {
73+ get: (): boolean => this.#isResolved,
74+ });
75+
76+ Object.defineProperty(onCancel, 'isRejected', {
77+ get: (): boolean => this.#isRejected,
6678 });
6779
6880 Object.defineProperty(onCancel, 'isCancelled', {
@@ -91,7 +103,7 @@ export class CancelablePromise<T> implements Promise<T> {
91103 }
92104
93105 public cancel(): void {
94- if (! this.#isPending || this.#isCancelled) {
106+ if (this.#isResolved || this.#isRejected || this.#isCancelled) {
95107 return;
96108 }
97109 this.#isCancelled = true;
@@ -101,10 +113,12 @@ export class CancelablePromise<T> implements Promise<T> {
101113 cancelHandler();
102114 }
103115 } catch (error) {
104- this.#reject?.( error);
116+ console.warn('Cancellation threw an error', error);
105117 return;
106118 }
107119 }
120+ this.#cancelHandlers.length = 0;
121+ this.#reject?.(new CancelError('Request aborted'));
108122 }
109123
110124 public get isCancelled(): boolean {
0 commit comments