Skip to content

Commit 57528cb

Browse files
authored
Update CancelablePromise.ts
1 parent ba1706b commit 57528cb

File tree

1 file changed

+67
-71
lines changed

1 file changed

+67
-71
lines changed
Lines changed: 67 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,30 @@
11
export class CancelError extends Error {
22
constructor(message: string) {
3-
super(message)
4-
this.name = "CancelError"
3+
super(message);
4+
this.name = "CancelError";
55
}
66

77
public get isCancelled(): boolean {
8-
return true
8+
return true;
99
}
1010
}
1111

1212
export interface OnCancel {
13-
readonly isResolved: boolean
14-
readonly isRejected: boolean
15-
readonly isCancelled: boolean
13+
readonly isResolved: boolean;
14+
readonly isRejected: boolean;
15+
readonly isCancelled: boolean;
1616

17-
(cancelHandler: () => void): void
17+
(cancelHandler: () => void): void;
1818
}
1919

2020
export class CancelablePromise<T> implements Promise<T> {
21-
private _isResolved: boolean
22-
private _isRejected: boolean
23-
private _isCancelled: boolean
24-
readonly cancelHandlers: (() => void)[]
25-
readonly promise: Promise<T>
26-
private _resolve?: (value: T | PromiseLike<T>) => void
27-
private _reject?: (reason?: unknown) => void
21+
private _isResolved: boolean = false;
22+
private _isRejected: boolean = false;
23+
private _isCancelled: boolean = false;
24+
private readonly cancelHandlers: (() => void)[] = [];
25+
private readonly promise: Promise<T>;
26+
private _resolve?: (value: T | PromiseLike<T>) => void;
27+
private _reject?: (reason?: unknown) => void;
2828

2929
constructor(
3030
executor: (
@@ -33,94 +33,90 @@ export class CancelablePromise<T> implements Promise<T> {
3333
onCancel: OnCancel,
3434
) => void,
3535
) {
36-
this._isResolved = false
37-
this._isRejected = false
38-
this._isCancelled = false
39-
this.cancelHandlers = []
4036
this.promise = new Promise<T>((resolve, reject) => {
41-
this._resolve = resolve
42-
this._reject = reject
43-
44-
const onResolve = (value: T | PromiseLike<T>): void => {
45-
if (this._isResolved || this._isRejected || this._isCancelled) {
46-
return
47-
}
48-
this._isResolved = true
49-
if (this._resolve) this._resolve(value)
50-
}
37+
this._resolve = resolve;
38+
this._reject = reject;
5139

52-
const onReject = (reason?: unknown): void => {
53-
if (this._isResolved || this._isRejected || this._isCancelled) {
54-
return
55-
}
56-
this._isRejected = true
57-
if (this._reject) this._reject(reason)
58-
}
40+
const onCancel = this.createOnCancel();
5941

60-
const onCancel = (cancelHandler: () => void): void => {
61-
if (this._isResolved || this._isRejected || this._isCancelled) {
62-
return
63-
}
64-
this.cancelHandlers.push(cancelHandler)
65-
}
42+
executor(this.createResolve(), this.createReject(), onCancel);
43+
});
44+
}
45+
46+
private createResolve(): (value: T | PromiseLike<T>) => void {
47+
return (value: T | PromiseLike<T>): void => {
48+
if (this.isFinalState()) return;
49+
this._isResolved = true;
50+
this._resolve?.(value);
51+
};
52+
}
6653

67-
Object.defineProperty(onCancel, "isResolved", {
68-
get: (): boolean => this._isResolved,
69-
})
54+
private createReject(): (reason?: unknown) => void {
55+
return (reason?: unknown): void => {
56+
if (this.isFinalState()) return;
57+
this._isRejected = true;
58+
this._reject?.(reason);
59+
};
60+
}
61+
62+
private createOnCancel(): OnCancel {
63+
const onCancel = ((cancelHandler: () => void): void => {
64+
if (this.isFinalState()) return;
65+
this.cancelHandlers.push(cancelHandler);
66+
}) as OnCancel;
7067

71-
Object.defineProperty(onCancel, "isRejected", {
72-
get: (): boolean => this._isRejected,
73-
})
68+
Object.defineProperties(onCancel, {
69+
isResolved: { get: () => this._isResolved },
70+
isRejected: { get: () => this._isRejected },
71+
isCancelled: { get: () => this._isCancelled },
72+
});
7473

75-
Object.defineProperty(onCancel, "isCancelled", {
76-
get: (): boolean => this._isCancelled,
77-
})
74+
return onCancel;
75+
}
7876

79-
return executor(onResolve, onReject, onCancel as OnCancel)
80-
})
77+
private isFinalState(): boolean {
78+
return this._isResolved || this._isRejected || this._isCancelled;
8179
}
8280

8381
get [Symbol.toStringTag]() {
84-
return "Cancellable Promise"
82+
return "Cancellable Promise";
8583
}
8684

8785
public then<TResult1 = T, TResult2 = never>(
8886
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
8987
onRejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,
9088
): Promise<TResult1 | TResult2> {
91-
return this.promise.then(onFulfilled, onRejected)
89+
return this.promise.then(onFulfilled, onRejected);
9290
}
9391

9492
public catch<TResult = never>(
9593
onRejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | null,
9694
): Promise<T | TResult> {
97-
return this.promise.catch(onRejected)
95+
return this.promise.catch(onRejected);
9896
}
9997

10098
public finally(onFinally?: (() => void) | null): Promise<T> {
101-
return this.promise.finally(onFinally)
99+
return this.promise.finally(onFinally);
102100
}
103101

104102
public cancel(): void {
105-
if (this._isResolved || this._isRejected || this._isCancelled) {
106-
return
107-
}
108-
this._isCancelled = true
109-
if (this.cancelHandlers.length) {
110-
try {
111-
for (const cancelHandler of this.cancelHandlers) {
112-
cancelHandler()
113-
}
114-
} catch (error) {
115-
console.warn("Cancellation threw an error", error)
116-
return
103+
if (this.isFinalState()) return;
104+
105+
this._isCancelled = true;
106+
107+
try {
108+
for (const cancelHandler of this.cancelHandlers) {
109+
cancelHandler();
117110
}
111+
} catch (error) {
112+
console.error("Error during cancellation:", error);
113+
} finally {
114+
this.cancelHandlers.length = 0;
115+
this._reject?.(new CancelError("Request aborted"));
118116
}
119-
this.cancelHandlers.length = 0
120-
if (this._reject) this._reject(new CancelError("Request aborted"))
121117
}
122118

123119
public get isCancelled(): boolean {
124-
return this._isCancelled
120+
return this._isCancelled;
125121
}
126122
}

0 commit comments

Comments
 (0)