/
Evt.asyncPipe.ts
98 lines (66 loc) 路 2.42 KB
/
Evt.asyncPipe.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
import { Evt } from "./Evt.ts";
type StatefulEvt<T> = import("./types/interfaces/index.ts").StatefulEvt<T>;
type StatefulReadonlyEvt<T> = import("./types/interfaces/index.ts").StatefulReadonlyEvt<T>;
type NonPostableEvt<T> = import("./types/interfaces/index.ts").NonPostableEvt<T>;
type UnpackEvt<T extends ({ [key: string]: any; } | import("./types/helper/UnpackEvt.ts").EvtLike<any>)> =
import("./types/helper/index.ts").UnpackEvt<T>;
type EvtLike<T> = import("./types/helper/UnpackEvt.ts").EvtLike<T> & {
attach(callback: (data: T) => void): void;
};
type UseVoidEvt<T> = import("./types/helper/SwapEvtType.ts").UseVoidEvt<T>;
type PromiseOrNot<T> = import("../tools/typeSafety/index.ts").PromiseOrNot<T>;
/**
* NOTE: Workaround until v2.0 where .pipe() will support async operators
* Usage example: https://stackblitz.com/edit/evt-async-op?file=index.ts
*
* When the argument is a StatefulEvt:
* If, wile asyncOp was running, the state of the source evt
* have changed then the result will be discarded.
*
* If the asyncOp complete synchronously (meaning it does not return
* a promise) then the result is synchronously transformed. (As with .pipe() )
*
* More usage example in src/test/test95.ts
*/
export function asyncPipe<E extends EvtLike<any>, U>(
evt: E,
asyncOp: (data: UnpackEvt<E>) => PromiseOrNot<[U] | null>
): UseVoidEvt<
E extends StatefulReadonlyEvt<any> ? StatefulEvt<U | undefined> :
E extends NonPostableEvt<any> ? Evt<U> :
EvtLike<U>
> {
const out = "state" in evt ?
Evt.create<UnpackEvt<E> | undefined>(undefined) :
Evt.create<UnpackEvt<E>>();
let currentCallCount = 0;
const onData = async (data: UnpackEvt<E>) => {
currentCallCount++;
const thisCallCount = currentCallCount;
const prOpResult = asyncOp(data);
let opResult: [U] | null;
if (
prOpResult !== null &&
"then" in prOpResult
) {
opResult = await prOpResult;
if (
"state" in evt &&
thisCallCount !== currentCallCount
) {
return;
}
} else {
opResult = prOpResult;
}
if (opResult === null) {
return;
}
out.post(opResult[0] as any);
};
evt.attach(onData);
if ("state" in evt) {
onData((evt as any).state);
}
return out as any;
}