repos / starfx

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

commit
4842df8
parent
0ba8393
author
Eric Bower
date
2023-09-18 01:06:19 +0000 UTC
refactor(redux): take strat

Trying to think about performance considerations with implementation
4 files changed,  +56, -56
M deps.ts
+1, -0
1@@ -3,6 +3,7 @@ export type {
2   Instruction,
3   Operation,
4   Port,
5+  Predicate,
6   Result,
7   Scope,
8   Signal,
M matcher.ts
+20, -9
 1@@ -1,42 +1,53 @@
 2 import type { AnyAction } from "./types.ts";
 3+import type { Predicate } from "./deps.ts";
 4 
 5 type ActionType = string;
 6 type GuardPredicate<G extends T, T = unknown> = (arg: T) => arg is G;
 7-type Predicate = (action: AnyAction) => boolean;
 8+type APredicate = (action: AnyAction) => boolean;
 9 type StringableActionCreator<A extends AnyAction = AnyAction> = {
10   (...args: unknown[]): A;
11   toString(): string;
12 };
13-type SubPattern = Predicate | StringableActionCreator | ActionType;
14+type SubPattern = APredicate | StringableActionCreator | ActionType;
15 export type Pattern = SubPattern | SubPattern[];
16 type ActionSubPattern<Guard extends AnyAction = AnyAction> =
17   | GuardPredicate<Guard, AnyAction>
18   | StringableActionCreator<Guard>
19-  | Predicate
20+  | APredicate
21   | ActionType;
22 export type ActionPattern<Guard extends AnyAction = AnyAction> =
23   | ActionSubPattern<Guard>
24   | ActionSubPattern<Guard>[];
25 
26-export function matcher(pattern: ActionPattern): (input: AnyAction) => boolean {
27+export function matcher(pattern: ActionPattern): Predicate<AnyAction> {
28   if (pattern === "*") {
29-    return (input: AnyAction) => !!input;
30+    return function* (input) {
31+      return !!input;
32+    };
33   }
34 
35   if (typeof pattern === "string") {
36-    return (input: AnyAction) => pattern === input.type;
37+    return function* (input) {
38+      return pattern === input.type;
39+    };
40   }
41 
42   if (Array.isArray(pattern)) {
43-    return (input: AnyAction) => pattern.some((p) => matcher(p)(input));
44+    return function* (input) {
45+      return pattern.some((p) => matcher(p)(input));
46+    };
47   }
48 
49   if (typeof pattern === "function" && Object.hasOwn(pattern, "toString")) {
50-    return (input: AnyAction) => pattern.toString() === input.type;
51+    return function* (input) {
52+      return pattern.toString() === input.type;
53+    };
54   }
55 
56   if (typeof pattern === "function") {
57-    return (input: AnyAction) => pattern(input) as boolean;
58+    return function* (input) {
59+      return pattern(input) as boolean;
60+    };
61   }
62 
63   throw new Error("invalid pattern");
M redux/fx.ts
+33, -41
  1@@ -1,5 +1,5 @@
  2 import type { Action, Operation, Signal } from "../deps.ts";
  3-import { createContext, each, spawn } from "../deps.ts";
  4+import { createContext, each, filter, spawn } from "../deps.ts";
  5 import { call } from "../fx/mod.ts";
  6 import { ActionPattern, matcher } from "../matcher.ts";
  7 import type { ActionWPayload, AnyAction } from "../types.ts";
  8@@ -11,6 +11,15 @@ export const ActionContext = createContext<Signal<Action, void>>(
  9 );
 10 export const StoreContext = createContext<StoreLike>("redux:store");
 11 
 12+export function* put(action: AnyAction | AnyAction[]) {
 13+  const store = yield* StoreContext;
 14+  if (Array.isArray(action)) {
 15+    action.map((act) => store.dispatch(act));
 16+  } else {
 17+    store.dispatch(action);
 18+  }
 19+}
 20+
 21 export function emit({
 22   signal,
 23   action,
 24@@ -28,35 +37,26 @@ export function emit({
 25   }
 26 }
 27 
 28-export function* once({
 29-  signal,
 30-  pattern,
 31-}: {
 32-  signal: Signal<Action, void>;
 33-  pattern: ActionPattern;
 34-}) {
 35-  for (const action of yield* each(signal.stream)) {
 36-    const match = matcher(pattern);
 37-    if (match(action)) {
 38-      return action;
 39-    }
 40-    yield* each.next;
 41-  }
 42-}
 43-
 44 export function* select<S, R>(selectorFn: (s: S) => R) {
 45   const store = yield* StoreContext;
 46   return selectorFn(store.getState() as S);
 47 }
 48 
 49+function* createPatternStream(pattern: ActionPattern) {
 50+  const signal = yield* ActionContext;
 51+  const match = matcher(pattern);
 52+  const fd = filter(match)(signal.stream);
 53+  return fd;
 54+}
 55+
 56 export function take<P>(pattern: ActionPattern): Operation<ActionWPayload<P>>;
 57 export function* take(pattern: ActionPattern): Operation<Action> {
 58-  const signal = yield* ActionContext;
 59-  const action = yield* once({
 60-    signal,
 61-    pattern,
 62-  });
 63-  return action as Action;
 64+  const fd = yield* createPatternStream(pattern);
 65+  for (const action of yield* each(fd)) {
 66+    return action;
 67+  }
 68+
 69+  return { type: "take failed, this should not be possible" };
 70 }
 71 
 72 export function* takeEvery<T>(
 73@@ -64,10 +64,10 @@ export function* takeEvery<T>(
 74   op: (action: Action) => Operation<T>,
 75 ) {
 76   return yield* spawn(function* (): Operation<void> {
 77-    while (true) {
 78-      const action = yield* take(pattern);
 79-      if (!action) continue;
 80+    const fd = yield* createPatternStream(pattern);
 81+    for (const action of yield* each(fd)) {
 82       yield* spawn(() => op(action));
 83+      yield* each.next;
 84     }
 85   });
 86 }
 87@@ -77,14 +77,15 @@ export function* takeLatest<T>(
 88   op: (action: Action) => Operation<T>,
 89 ) {
 90   return yield* spawn(function* (): Operation<void> {
 91+    const fd = yield* createPatternStream(pattern);
 92     let lastTask;
 93-    while (true) {
 94-      const action = yield* take(pattern);
 95+
 96+    for (const action of yield* each(fd)) {
 97       if (lastTask) {
 98         yield* lastTask.halt();
 99       }
100-      if (!action) continue;
101       lastTask = yield* spawn(() => op(action));
102+      yield* each.next;
103     }
104   });
105 }
106@@ -95,20 +96,11 @@ export function* takeLeading<T>(
107   op: (action: Action) => Operation<T>,
108 ) {
109   return yield* spawn(function* (): Operation<void> {
110-    while (true) {
111-      const action = yield* take(pattern);
112-      if (!action) continue;
113+    const fd = yield* createPatternStream(pattern);
114+    for (const action of yield* each(fd)) {
115       yield* call(() => op(action));
116+      yield* each.next;
117     }
118   });
119 }
120 export const leading = takeLeading;
121-
122-export function* put(action: AnyAction | AnyAction[]) {
123-  const store = yield* StoreContext;
124-  if (Array.isArray(action)) {
125-    action.map((act) => store.dispatch(act));
126-  } else {
127-    store.dispatch(action);
128-  }
129-}
M store/fx.ts
+2, -6
 1@@ -58,14 +58,10 @@ export function* put(action: AnyAction | AnyAction[]) {
 2 }
 3 
 4 export function* useActions(pattern: ActionPattern): Stream<AnyAction, void> {
 5-  const match = matcher(pattern);
 6   const { output } = yield* ActionContext;
 7-  // deno-lint-ignore require-yield
 8-  function* fn(a: AnyAction) {
 9-    return match(a);
10-  }
11+  const match = matcher(pattern);
12   // return a subscription to the filtered actions.
13-  const result = yield* filter(fn)(output);
14+  const result = yield* filter(match)(output);
15   return result;
16 }
17