/
PCancelable.ts
86 lines (77 loc) · 1.95 KB
/
PCancelable.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
class CancelError extends Error {
constructor() {
super('Promise was canceled');
this.name = 'CancelError';
}
}
export default class PCancelable<T> implements PromiseLike<T> {
private _pending = true;
private _canceled = false;
private _promise: Promise<T>;
private _cancel?: () => void;
private _reject: (reason?: unknown) => void = () => {};
constructor(
executor: (
onCancel: (cancelHandler: () => void) => void,
resolve: (value: T | PromiseLike<T>) => void,
reject: (reason?: unknown) => void,
) => void,
) {
this._promise = new Promise((resolve, reject) => {
this._reject = reject;
return executor(
fn => {
this._cancel = fn;
},
val => {
this._pending = false;
resolve(val);
},
err => {
this._pending = false;
reject(err);
},
);
});
}
then<TResult1 = T, TResult2 = never>(
onFulfilled?:
| ((value: T) => TResult1 | PromiseLike<TResult1>)
| undefined
| null,
onRejected?:
| ((reason: unknown) => TResult2 | PromiseLike<TResult2>)
| undefined
| null,
): Promise<TResult1 | TResult2> {
return this._promise.then(onFulfilled, onRejected);
}
catch<TResult>(
onRejected?:
| ((reason: unknown) => TResult | PromiseLike<TResult>)
| undefined
| null,
): Promise<T | TResult> {
return this._promise.catch(onRejected);
}
cancel(): void {
if (!this._pending || this._canceled) {
return;
}
if (typeof this._cancel === 'function') {
try {
this._cancel();
} catch (err) {
this._reject(err);
}
}
this._canceled = true;
this._reject(new CancelError());
}
}