repos / starfx

supercharged async flow control library.
git clone https://github.com/neurosnap/starfx.git

commit
e18a41f
parent
e0dffb4
author
Eric Bower
date
2023-09-16 02:29:18 +0000 UTC
chore: export supervisors
3 files changed,  +87, -2
M redux/mod.ts
+1, -0
1@@ -4,3 +4,4 @@ export * from "./middleware.ts";
2 export * from "./types.ts";
3 export type { ActionWPayload, AnyAction, AnyState } from "../types.ts";
4 export { createSelector } from "../deps.ts";
5+export * from "./supervisor.ts";
A redux/supervisor.ts
+76, -0
 1@@ -0,0 +1,76 @@
 2+import { call, race } from "../fx/mod.ts";
 3+import { take, takeLatest, takeLeading } from "./fx.ts";
 4+import { Operation, sleep, spawn, Task } from "../deps.ts";
 5+import type { ActionWPayload, AnyAction, OpFn } from "../types.ts";
 6+import type { CreateActionPayload } from "../query/mod.ts";
 7+
 8+const MS = 1000;
 9+const SECONDS = 1 * MS;
10+const MINUTES = 60 * SECONDS;
11+
12+export function* latest(action: string, saga: any) {
13+  yield takeLatest(`${action}`, saga);
14+}
15+
16+export function* leading(action: string, saga: any) {
17+  yield takeLeading(`${action}`, saga);
18+}
19+
20+export function poll(parentTimer: number = 5 * 1000, cancelType?: string) {
21+  return function* poller<T>(
22+    actionType: string,
23+    op: (action: AnyAction) => Operation<T>,
24+  ): Operation<T> {
25+    const cancel = cancelType || actionType;
26+    function* fire(action: { type: string }, timer: number) {
27+      while (true) {
28+        yield* call(() => op(action));
29+        yield* sleep(timer);
30+      }
31+    }
32+
33+    while (true) {
34+      const action = yield* take<{ timer?: number }>(actionType);
35+      const timer = action.payload?.timer || parentTimer;
36+      yield* race({
37+        fire: () => call(() => fire(action, timer)),
38+        cancel: () => take(`${cancel}`),
39+      });
40+    }
41+  };
42+}
43+
44+/**
45+ * timer() will create a cache timer for each `key` inside
46+ * of a saga-query api endpoint.  `key` is a hash of the action type and payload.
47+ *
48+ * Why do we want this?  If we have an api endpoint to fetch a single app: `fetchApp({ id: 1 })`
49+ * if we don't set a timer per key then all calls to `fetchApp` will be on a timer.
50+ * So if we call `fetchApp({ id: 1 })` and then `fetchApp({ id: 2 })` if we use a normal
51+ * cache timer then the second call will not send an http request.
52+ */
53+export function timer(timer: number = 5 * MINUTES) {
54+  return function* onTimer(
55+    actionType: string,
56+    op: (action: AnyAction) => OpFn,
57+  ) {
58+    const map: { [key: string]: Task<unknown> } = {};
59+
60+    function* activate(action: ActionWPayload<CreateActionPayload>) {
61+      yield* call(() => op(action));
62+      yield* sleep(timer);
63+      delete map[action.payload.key];
64+    }
65+
66+    while (true) {
67+      const action = yield* take<CreateActionPayload>(`${actionType}`);
68+      const key = action.payload.key;
69+      if (!map[key]) {
70+        const task = yield* spawn(function* () {
71+          yield* activate(action);
72+        });
73+        map[key] = task;
74+      }
75+    }
76+  };
77+}
M store/supervisor.ts
+10, -2
 1@@ -1,13 +1,21 @@
 2 import { call, race } from "../fx/mod.ts";
 3-import { take } from "../store/mod.ts";
 4+import { take, takeLatest, takeLeading } from "./fx.ts";
 5 import { Operation, sleep, spawn, Task } from "../deps.ts";
 6 import type { ActionWPayload, AnyAction, OpFn } from "../types.ts";
 7-import type { CreateActionPayload } from "../query/types.ts";
 8+import type { CreateActionPayload } from "../query/mod.ts";
 9 
10 const MS = 1000;
11 const SECONDS = 1 * MS;
12 const MINUTES = 60 * SECONDS;
13 
14+export function* latest(action: string, saga: any) {
15+  yield takeLatest(`${action}`, saga);
16+}
17+
18+export function* leading(action: string, saga: any) {
19+  yield takeLeading(`${action}`, saga);
20+}
21+
22 export function poll(parentTimer: number = 5 * 1000, cancelType?: string) {
23   return function* poller<T>(
24     actionType: string,