-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpromisify.ts
81 lines (75 loc) · 2.41 KB
/
promisify.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
import type { Result } from "./types.ts";
export interface CallbackOptions {
forceQueued?: boolean;
}
export interface PromisifyOptions {
/** callbackから次の値を取り出すまでに実行されたcallbackの結果を最新でいくつまで保持するかを表す値
*
* - 最後に実行された値だけを残したいときは`1`を指定する
* - 一つも残したくないときは`0`を指定する
* - 全部残したいときはundefinedを指定する
* - これが既定の動作
*
* @default undefined
*/
maxQueued?: number | undefined;
}
/** callbackをPromiseに変換するやつ
*
* @return 左から順に、Promiseを返すやつ、正常値を受け取るcallback、異常値を受け取るcallback
*/
export function promisify<T, E = unknown>(
options?: PromisifyOptions,
): readonly [
() => Promise<T>,
(value: T, options?: CallbackOptions) => void,
(reason: E, options?: CallbackOptions) => void,
] {
const maxQueued = options?.maxQueued === undefined
? undefined
: Math.max(0, options.maxQueued);
const queue = [] as Result<T, E>[];
const queue2 = [] as Result<T, E>[];
let _resolve: ((value: T, options?: CallbackOptions) => void) | undefined;
let _reject: ((value: E, options?: CallbackOptions) => void) | undefined;
/** queueから一つ取り出す。空なら_resolveをセットする */
const waitForSettled = async () => {
if (maxQueued !== undefined) queue.splice(0, queue.length - maxQueued);
const value = queue2.shift() ?? queue.shift();
if (value) {
if (value.success) return value.value;
throw value.reason;
}
return await new Promise<T>(
(res, rej) => {
_resolve = res;
_reject = rej;
},
);
};
const resolve = (value: T, options?: CallbackOptions) => {
if (!_resolve) {
if (options?.forceQueued) {
queue2.push({ success: true, value });
} else {
queue.push({ success: true, value });
}
return;
}
_resolve(value);
_resolve = _reject = undefined;
};
const reject = (reason: E, options?: CallbackOptions) => {
if (!_reject) {
if (options?.forceQueued) {
queue2.push({ success: false, reason });
} else {
queue.push({ success: false, reason });
}
return;
}
_reject(reason);
_resolve = _reject = undefined;
};
return [waitForSettled, resolve, reject] as const;
}