/
index.ts
108 lines (90 loc) · 2.12 KB
/
index.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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
export const JACKALL_ERROR_CODE = {
NOMATCH_VERSION: "Version don't match." as const,
FAILED_ASYNCTASK: "Failed to async task." as const,
};
interface JackallOK<T> {
ok: true;
value: T;
}
interface JackallNG {
ok: false;
code: Exclude<
typeof JACKALL_ERROR_CODE[keyof typeof JACKALL_ERROR_CODE],
typeof JACKALL_ERROR_CODE.FAILED_ASYNCTASK
>;
value: Error;
}
interface JackallExpectedNG<T> {
ok: false;
code: typeof JACKALL_ERROR_CODE.FAILED_ASYNCTASK;
value: T;
}
type JackallResult<T> = JackallOK<T> | JackallNG | JackallExpectedNG<T>;
class Jackall<T extends string> {
private resources: {
[K in T]?: {
version: number;
living: boolean /* TODO: Sweep. */;
};
} = {};
constructor() {}
async acquire<A>(key: T, d: Promise<A>): Promise<JackallResult<A>> {
const version = this.increment(key);
const res = await this.wait(d);
this.kill(key);
if (version !== this.getVersion(key)) {
return {
ok: false,
code: JACKALL_ERROR_CODE.NOMATCH_VERSION,
value: new Error(`Jackall Error: ${JACKALL_ERROR_CODE.NOMATCH_VERSION}`),
};
}
if (!res.ok) {
return {
ok: false,
code: JACKALL_ERROR_CODE.FAILED_ASYNCTASK,
value: res.value,
};
}
return {
ok: true,
value: res.value,
};
}
private async wait<A>(
d: Promise<A>
): Promise<{ ok: true; value: A } | { ok: false; value: any }> {
try {
return {
ok: true,
value: await d,
};
} catch (e) {
return {
ok: false,
value: e,
};
}
}
private getVersion(key: T): number {
const res = this.resources[key];
return res?.version || 0;
}
private increment(key: T) {
const version = this.getVersion(key) + 1;
this.resources[key] = {
version,
living: true,
};
return version;
}
private kill(key: T) {
const res = this.resources[key];
this.resources[key] = {
...res,
living: false,
};
}
}
export const jackall = new Jackall<string>();
export const makeJackall = <T extends string>() => new Jackall<T>();