Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ulixee
GitHub Repository: ulixee/secret-agent
Path: blob/main/commons/Resolvable.ts
1028 views
1
import IResolvablePromise from '@secret-agent/interfaces/IResolvablePromise';
2
import TimeoutError from './interfaces/TimeoutError';
3
4
export default class Resolvable<T = any> implements IResolvablePromise<T>, PromiseLike<T> {
5
public isResolved = false;
6
public resolved: T;
7
public promise: Promise<T>;
8
public readonly timeout: NodeJS.Timeout;
9
public stack: string;
10
11
private resolveFn: (value: T | PromiseLike<T>) => void;
12
private rejectFn: (error?: Error) => void;
13
14
constructor(timeoutMillis?: number, timeoutMessage?: string) {
15
// get parent stack
16
this.stack = new Error('').stack.slice(8);
17
18
this.promise = new Promise<T>((resolve, reject) => {
19
this.resolveFn = resolve;
20
this.rejectFn = reject;
21
});
22
23
if (timeoutMillis !== undefined && timeoutMillis !== null) {
24
this.timeout = setTimeout(
25
this.rejectWithTimeout.bind(this, timeoutMessage),
26
timeoutMillis,
27
).unref();
28
}
29
this.resolve = this.resolve.bind(this);
30
this.reject = this.reject.bind(this);
31
}
32
33
public resolve(value: T | PromiseLike<T>): void {
34
if (this.isResolved) return;
35
36
this.resolveFn(value);
37
38
Promise.resolve(value)
39
// eslint-disable-next-line promise/always-return
40
.then(x => {
41
this.isResolved = true;
42
this.resolved = x;
43
this.clean();
44
this.stack = null;
45
})
46
.catch(this.reject);
47
}
48
49
public reject(error: Error): void {
50
if (this.isResolved) return;
51
this.isResolved = true;
52
this.rejectFn(error);
53
this.clean();
54
}
55
56
public toJSON(): object {
57
return {
58
isResolved: this.isResolved,
59
resolved: this.resolved,
60
};
61
}
62
63
public then<TResult1 = T, TResult2 = never>(
64
onfulfilled?: (value: T) => TResult1 | PromiseLike<TResult1>,
65
onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>,
66
): Promise<TResult1 | TResult2> {
67
return this.promise.then(onfulfilled, onrejected);
68
}
69
70
public catch<TResult = never>(
71
onrejected?: (reason: any) => TResult | PromiseLike<TResult>,
72
): Promise<T | TResult> {
73
return this.promise.catch(onrejected);
74
}
75
76
public finally(onfinally?: () => void): Promise<T> {
77
return this.promise.finally(onfinally);
78
}
79
80
private clean(): void {
81
clearTimeout(this.timeout);
82
this.resolveFn = null;
83
this.rejectFn = null;
84
}
85
86
private rejectWithTimeout(message: string): void {
87
const error = new TimeoutError(message);
88
error.stack = `TimeoutError: ${message}\n${this.stack}`;
89
this.reject(error);
90
}
91
}
92
93