@ersbeth/picoflow 0.2.4 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/plans/update-js-e795d61b.plan.md +567 -0
- package/.gitlab-ci.yml +24 -0
- package/.vscode/settings.json +3 -3
- package/CHANGELOG.md +51 -0
- package/IMPLEMENTATION_GUIDE.md +1578 -0
- package/README.md +62 -25
- package/biome.json +32 -32
- package/dist/picoflow.js +610 -436
- package/dist/types/advanced/array.d.ts +0 -6
- package/dist/types/advanced/array.d.ts.map +1 -1
- package/dist/types/advanced/index.d.ts +5 -5
- package/dist/types/advanced/index.d.ts.map +1 -1
- package/dist/types/advanced/map.d.ts +114 -23
- package/dist/types/advanced/map.d.ts.map +1 -1
- package/dist/types/advanced/resource.d.ts +51 -12
- package/dist/types/advanced/resource.d.ts.map +1 -1
- package/dist/types/advanced/resourceAsync.d.ts +28 -13
- package/dist/types/advanced/resourceAsync.d.ts.map +1 -1
- package/dist/types/advanced/stream.d.ts +74 -16
- package/dist/types/advanced/stream.d.ts.map +1 -1
- package/dist/types/advanced/streamAsync.d.ts +69 -15
- package/dist/types/advanced/streamAsync.d.ts.map +1 -1
- package/dist/types/basic/constant.d.ts +44 -16
- package/dist/types/basic/constant.d.ts.map +1 -1
- package/dist/types/basic/derivation.d.ts +73 -24
- package/dist/types/basic/derivation.d.ts.map +1 -1
- package/dist/types/basic/disposable.d.ts +65 -6
- package/dist/types/basic/disposable.d.ts.map +1 -1
- package/dist/types/basic/effect.d.ts +27 -16
- package/dist/types/basic/effect.d.ts.map +1 -1
- package/dist/types/basic/index.d.ts +7 -8
- package/dist/types/basic/index.d.ts.map +1 -1
- package/dist/types/basic/observable.d.ts +62 -13
- package/dist/types/basic/observable.d.ts.map +1 -1
- package/dist/types/basic/signal.d.ts +35 -6
- package/dist/types/basic/signal.d.ts.map +1 -1
- package/dist/types/basic/state.d.ts +25 -4
- package/dist/types/basic/state.d.ts.map +1 -1
- package/dist/types/basic/trackingContext.d.ts +33 -0
- package/dist/types/basic/trackingContext.d.ts.map +1 -0
- package/dist/types/creators.d.ts +271 -26
- package/dist/types/creators.d.ts.map +1 -1
- package/dist/types/index.d.ts +60 -7
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/solid/converters.d.ts +5 -5
- package/dist/types/solid/converters.d.ts.map +1 -1
- package/dist/types/solid/index.d.ts +2 -2
- package/dist/types/solid/index.d.ts.map +1 -1
- package/dist/types/solid/primitives.d.ts +96 -4
- package/dist/types/solid/primitives.d.ts.map +1 -1
- package/docs/.vitepress/config.mts +110 -0
- package/docs/api/classes/FlowArray.md +489 -0
- package/docs/api/classes/FlowConstant.md +350 -0
- package/docs/api/classes/FlowDerivation.md +334 -0
- package/docs/api/classes/FlowEffect.md +100 -0
- package/docs/api/classes/FlowMap.md +512 -0
- package/docs/api/classes/FlowObservable.md +306 -0
- package/docs/api/classes/FlowResource.md +380 -0
- package/docs/api/classes/FlowResourceAsync.md +362 -0
- package/docs/api/classes/FlowSignal.md +160 -0
- package/docs/api/classes/FlowState.md +368 -0
- package/docs/api/classes/FlowStream.md +367 -0
- package/docs/api/classes/FlowStreamAsync.md +364 -0
- package/docs/api/classes/SolidDerivation.md +75 -0
- package/docs/api/classes/SolidResource.md +91 -0
- package/docs/api/classes/SolidState.md +71 -0
- package/docs/api/classes/TrackingContext.md +33 -0
- package/docs/api/functions/array.md +58 -0
- package/docs/api/functions/constant.md +45 -0
- package/docs/api/functions/derivation.md +53 -0
- package/docs/api/functions/effect.md +49 -0
- package/docs/api/functions/from.md +220 -0
- package/docs/api/functions/isDisposable.md +49 -0
- package/docs/api/functions/map.md +57 -0
- package/docs/api/functions/resource.md +52 -0
- package/docs/api/functions/resourceAsync.md +50 -0
- package/docs/api/functions/signal.md +36 -0
- package/docs/api/functions/state.md +47 -0
- package/docs/api/functions/stream.md +53 -0
- package/docs/api/functions/streamAsync.md +50 -0
- package/docs/api/index.md +118 -0
- package/docs/api/interfaces/FlowDisposable.md +65 -0
- package/docs/api/interfaces/SolidObservable.md +19 -0
- package/docs/api/type-aliases/FlowArrayAction.md +49 -0
- package/docs/api/type-aliases/FlowStreamDisposer.md +15 -0
- package/docs/api/type-aliases/FlowStreamSetter.md +27 -0
- package/docs/api/type-aliases/FlowStreamUpdater.md +32 -0
- package/docs/api/type-aliases/NotPromise.md +18 -0
- package/docs/api/type-aliases/SolidGetter.md +17 -0
- package/docs/api/typedoc-sidebar.json +1 -0
- package/docs/examples/examples.md +2313 -0
- package/docs/examples/patterns.md +649 -0
- package/docs/guide/advanced/disposal.md +426 -0
- package/docs/guide/advanced/solidjs.md +221 -0
- package/docs/guide/advanced/upgrading.md +464 -0
- package/docs/guide/introduction/concepts.md +56 -0
- package/docs/guide/introduction/conventions.md +61 -0
- package/docs/guide/introduction/getting-started.md +134 -0
- package/docs/guide/introduction/lifecycle.md +371 -0
- package/docs/guide/primitives/array.md +400 -0
- package/docs/guide/primitives/constant.md +380 -0
- package/docs/guide/primitives/derivations.md +348 -0
- package/docs/guide/primitives/effects.md +458 -0
- package/docs/guide/primitives/map.md +387 -0
- package/docs/guide/primitives/overview.md +175 -0
- package/docs/guide/primitives/resources.md +858 -0
- package/docs/guide/primitives/signal.md +259 -0
- package/docs/guide/primitives/state.md +368 -0
- package/docs/guide/primitives/streams.md +931 -0
- package/docs/index.md +47 -0
- package/docs/public/logo.svg +1 -0
- package/package.json +57 -41
- package/src/advanced/array.ts +208 -210
- package/src/advanced/index.ts +7 -7
- package/src/advanced/map.ts +178 -68
- package/src/advanced/resource.ts +87 -43
- package/src/advanced/resourceAsync.ts +62 -42
- package/src/advanced/stream.ts +113 -50
- package/src/advanced/streamAsync.ts +120 -61
- package/src/basic/constant.ts +82 -49
- package/src/basic/derivation.ts +128 -84
- package/src/basic/disposable.ts +74 -15
- package/src/basic/effect.ts +85 -77
- package/src/basic/index.ts +7 -8
- package/src/basic/observable.ts +94 -36
- package/src/basic/signal.ts +133 -105
- package/src/basic/state.ts +46 -25
- package/src/basic/trackingContext.ts +45 -0
- package/src/creators.ts +297 -54
- package/src/index.ts +96 -43
- package/src/solid/converters.ts +186 -67
- package/src/solid/index.ts +8 -2
- package/src/solid/primitives.ts +167 -65
- package/test/array.test.ts +592 -612
- package/test/constant.test.ts +31 -33
- package/test/derivation.test.ts +531 -536
- package/test/effect.test.ts +21 -21
- package/test/map.test.ts +233 -137
- package/test/resource.test.ts +119 -121
- package/test/resourceAsync.test.ts +98 -100
- package/test/signal.test.ts +51 -55
- package/test/state.test.ts +186 -168
- package/test/stream.test.ts +189 -189
- package/test/streamAsync.test.ts +186 -186
- package/tsconfig.json +19 -18
- package/typedoc.json +37 -0
- package/vite.config.ts +23 -23
- package/vitest.config.ts +7 -7
- package/api/doc/index.md +0 -31
- package/api/doc/picoflow.array.md +0 -55
- package/api/doc/picoflow.constant.md +0 -55
- package/api/doc/picoflow.derivation.md +0 -55
- package/api/doc/picoflow.effect.md +0 -55
- package/api/doc/picoflow.flowarray._constructor_.md +0 -49
- package/api/doc/picoflow.flowarray._lastaction.md +0 -13
- package/api/doc/picoflow.flowarray.clear.md +0 -17
- package/api/doc/picoflow.flowarray.dispose.md +0 -55
- package/api/doc/picoflow.flowarray.get.md +0 -19
- package/api/doc/picoflow.flowarray.length.md +0 -13
- package/api/doc/picoflow.flowarray.md +0 -273
- package/api/doc/picoflow.flowarray.pop.md +0 -17
- package/api/doc/picoflow.flowarray.push.md +0 -53
- package/api/doc/picoflow.flowarray.set.md +0 -53
- package/api/doc/picoflow.flowarray.setitem.md +0 -69
- package/api/doc/picoflow.flowarray.shift.md +0 -17
- package/api/doc/picoflow.flowarray.splice.md +0 -85
- package/api/doc/picoflow.flowarray.unshift.md +0 -53
- package/api/doc/picoflow.flowarrayaction.md +0 -37
- package/api/doc/picoflow.flowconstant._constructor_.md +0 -49
- package/api/doc/picoflow.flowconstant.get.md +0 -25
- package/api/doc/picoflow.flowconstant.md +0 -88
- package/api/doc/picoflow.flowderivation._constructor_.md +0 -49
- package/api/doc/picoflow.flowderivation.get.md +0 -23
- package/api/doc/picoflow.flowderivation.md +0 -86
- package/api/doc/picoflow.flowdisposable.dispose.md +0 -55
- package/api/doc/picoflow.flowdisposable.md +0 -43
- package/api/doc/picoflow.floweffect._constructor_.md +0 -54
- package/api/doc/picoflow.floweffect.dispose.md +0 -21
- package/api/doc/picoflow.floweffect.disposed.md +0 -13
- package/api/doc/picoflow.floweffect.md +0 -131
- package/api/doc/picoflow.flowgetter.md +0 -15
- package/api/doc/picoflow.flowmap._lastdeleted.md +0 -21
- package/api/doc/picoflow.flowmap._lastset.md +0 -21
- package/api/doc/picoflow.flowmap.delete.md +0 -61
- package/api/doc/picoflow.flowmap.md +0 -133
- package/api/doc/picoflow.flowmap.setat.md +0 -77
- package/api/doc/picoflow.flowobservable.get.md +0 -19
- package/api/doc/picoflow.flowobservable.md +0 -68
- package/api/doc/picoflow.flowobservable.subscribe.md +0 -55
- package/api/doc/picoflow.flowresource._constructor_.md +0 -49
- package/api/doc/picoflow.flowresource.fetch.md +0 -27
- package/api/doc/picoflow.flowresource.get.md +0 -23
- package/api/doc/picoflow.flowresource.md +0 -100
- package/api/doc/picoflow.flowresourceasync._constructor_.md +0 -49
- package/api/doc/picoflow.flowresourceasync.fetch.md +0 -27
- package/api/doc/picoflow.flowresourceasync.get.md +0 -23
- package/api/doc/picoflow.flowresourceasync.md +0 -100
- package/api/doc/picoflow.flowsignal.dispose.md +0 -59
- package/api/doc/picoflow.flowsignal.disposed.md +0 -18
- package/api/doc/picoflow.flowsignal.md +0 -112
- package/api/doc/picoflow.flowsignal.trigger.md +0 -21
- package/api/doc/picoflow.flowstate.md +0 -52
- package/api/doc/picoflow.flowstate.set.md +0 -61
- package/api/doc/picoflow.flowstream._constructor_.md +0 -49
- package/api/doc/picoflow.flowstream.dispose.md +0 -21
- package/api/doc/picoflow.flowstream.get.md +0 -23
- package/api/doc/picoflow.flowstream.md +0 -100
- package/api/doc/picoflow.flowstreamasync._constructor_.md +0 -54
- package/api/doc/picoflow.flowstreamasync.dispose.md +0 -21
- package/api/doc/picoflow.flowstreamasync.get.md +0 -23
- package/api/doc/picoflow.flowstreamasync.md +0 -100
- package/api/doc/picoflow.flowstreamdisposer.md +0 -13
- package/api/doc/picoflow.flowstreamsetter.md +0 -13
- package/api/doc/picoflow.flowstreamupdater.md +0 -19
- package/api/doc/picoflow.flowwatcher.md +0 -15
- package/api/doc/picoflow.from.md +0 -55
- package/api/doc/picoflow.from_1.md +0 -55
- package/api/doc/picoflow.from_2.md +0 -55
- package/api/doc/picoflow.from_3.md +0 -55
- package/api/doc/picoflow.from_4.md +0 -55
- package/api/doc/picoflow.from_5.md +0 -55
- package/api/doc/picoflow.isdisposable.md +0 -55
- package/api/doc/picoflow.map.md +0 -59
- package/api/doc/picoflow.md +0 -544
- package/api/doc/picoflow.resource.md +0 -55
- package/api/doc/picoflow.resourceasync.md +0 -55
- package/api/doc/picoflow.signal.md +0 -19
- package/api/doc/picoflow.solidderivation._constructor_.md +0 -49
- package/api/doc/picoflow.solidderivation.get.md +0 -13
- package/api/doc/picoflow.solidderivation.md +0 -94
- package/api/doc/picoflow.solidgetter.md +0 -13
- package/api/doc/picoflow.solidobservable.get.md +0 -13
- package/api/doc/picoflow.solidobservable.md +0 -57
- package/api/doc/picoflow.solidresource._constructor_.md +0 -49
- package/api/doc/picoflow.solidresource.get.md +0 -13
- package/api/doc/picoflow.solidresource.latest.md +0 -13
- package/api/doc/picoflow.solidresource.md +0 -157
- package/api/doc/picoflow.solidresource.refetch.md +0 -13
- package/api/doc/picoflow.solidresource.state.md +0 -13
- package/api/doc/picoflow.solidstate._constructor_.md +0 -49
- package/api/doc/picoflow.solidstate.get.md +0 -13
- package/api/doc/picoflow.solidstate.md +0 -115
- package/api/doc/picoflow.solidstate.set.md +0 -13
- package/api/doc/picoflow.state.md +0 -55
- package/api/doc/picoflow.stream.md +0 -55
- package/api/doc/picoflow.streamasync.md +0 -55
- package/api/picoflow.public.api.md +0 -244
- package/api-extractor.json +0 -61
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
# Lifecycle
|
|
2
|
+
|
|
3
|
+
Understanding PicoFlow's internal execution model is key to mastering reactive programming. This guide walks through exactly what happens during creation, updates, and disposal using a concrete example.
|
|
4
|
+
|
|
5
|
+
## The Example
|
|
6
|
+
|
|
7
|
+
Throughout this guide, we'll use this simple but complete example:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
const $a = state(1)
|
|
11
|
+
const $b = state(2)
|
|
12
|
+
const $c = derivation((t) => $a.get(t) + $b.get(t))
|
|
13
|
+
effect((t) => console.log('C =', $c.get(t)))
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
This example demonstrates:
|
|
17
|
+
- **Two states**: `$a` and `$b`
|
|
18
|
+
- **One derivation**: `$c` that computes the sum
|
|
19
|
+
- **One effect**: that logs the result
|
|
20
|
+
|
|
21
|
+
Let's see what happens at each stage of the lifecycle.
|
|
22
|
+
|
|
23
|
+
## Creation Flow
|
|
24
|
+
|
|
25
|
+
When you run the example code, here's the exact sequence of events:
|
|
26
|
+
|
|
27
|
+
### Step-by-Step
|
|
28
|
+
|
|
29
|
+
1. **`state(1)` creates `$a`**
|
|
30
|
+
- A new `FlowState` instance is created
|
|
31
|
+
- Internal value is set to `1`
|
|
32
|
+
- No dependencies yet
|
|
33
|
+
|
|
34
|
+
2. **`state(2)` creates `$b`**
|
|
35
|
+
- Another `FlowState` instance is created
|
|
36
|
+
- Internal value is set to `2`
|
|
37
|
+
- No dependencies yet
|
|
38
|
+
|
|
39
|
+
3. **`derivation(...)` creates `$c`**
|
|
40
|
+
- A new `FlowDerivation` instance is created
|
|
41
|
+
- The compute function is stored but **NOT executed** (lazy evaluation)
|
|
42
|
+
- A `TrackingContext` is created for the derivation
|
|
43
|
+
- Still no computed value yet
|
|
44
|
+
|
|
45
|
+
4. **`effect(...)` creates the effect**
|
|
46
|
+
- A new `FlowEffect` instance is created
|
|
47
|
+
- A `TrackingContext` is created for the effect
|
|
48
|
+
- **The effect executes immediately** (not lazy!)
|
|
49
|
+
- During execution:
|
|
50
|
+
- Effect calls `$c.get(t)`
|
|
51
|
+
- This triggers `$c` to initialize (first access)
|
|
52
|
+
- `$c` runs its compute function with its tracking context
|
|
53
|
+
- Compute calls `$a.get(t)` → returns `1`, registers `$a` as dependency of `$c`
|
|
54
|
+
- Compute calls `$b.get(t)` → returns `2`, registers `$b` as dependency of `$c`
|
|
55
|
+
- Compute returns `3`
|
|
56
|
+
- Effect's `$c.get(t)` returns `3`, registers `$c` as dependency of effect
|
|
57
|
+
- Effect logs: `"C = 3"`
|
|
58
|
+
|
|
59
|
+
### Creation Sequence Diagram
|
|
60
|
+
|
|
61
|
+
```mermaid
|
|
62
|
+
sequenceDiagram
|
|
63
|
+
participant User
|
|
64
|
+
participant $a as $a (State)
|
|
65
|
+
participant $b as $b (State)
|
|
66
|
+
participant $c as $c (Derivation)
|
|
67
|
+
participant Effect
|
|
68
|
+
participant Context
|
|
69
|
+
|
|
70
|
+
User->>$a: state(1)
|
|
71
|
+
activate $a
|
|
72
|
+
Note over $a: Store value: 1
|
|
73
|
+
deactivate $a
|
|
74
|
+
|
|
75
|
+
User->>$b: state(2)
|
|
76
|
+
activate $b
|
|
77
|
+
Note over $b: Store value: 2
|
|
78
|
+
deactivate $b
|
|
79
|
+
|
|
80
|
+
User->>$c: derivation((t) => ...)
|
|
81
|
+
activate $c
|
|
82
|
+
Note over $c: Store compute fn<br/>NOT executed yet<br/>(lazy)
|
|
83
|
+
deactivate $c
|
|
84
|
+
|
|
85
|
+
User->>Effect: effect((t) => ...)
|
|
86
|
+
activate Effect
|
|
87
|
+
Note over Effect: Create & execute<br/>immediately
|
|
88
|
+
|
|
89
|
+
Effect->>$c: get(t)
|
|
90
|
+
activate $c
|
|
91
|
+
Note over $c: First access!<br/>Initialize & compute
|
|
92
|
+
|
|
93
|
+
$c->>$a: get(t)
|
|
94
|
+
activate $a
|
|
95
|
+
$a-->>$c: 1
|
|
96
|
+
$a->>$c: Register as listener
|
|
97
|
+
Note over $a,$c: $c now depends on $a
|
|
98
|
+
deactivate $a
|
|
99
|
+
|
|
100
|
+
$c->>$b: get(t)
|
|
101
|
+
activate $b
|
|
102
|
+
$b-->>$c: 2
|
|
103
|
+
$b->>$c: Register as listener
|
|
104
|
+
Note over $b,$c: $c now depends on $b
|
|
105
|
+
deactivate $b
|
|
106
|
+
|
|
107
|
+
Note over $c: Compute: 1 + 2 = 3
|
|
108
|
+
$c-->>Effect: 3
|
|
109
|
+
$c->>Effect: Register as effect
|
|
110
|
+
Note over $c,Effect: Effect depends on $c
|
|
111
|
+
deactivate $c
|
|
112
|
+
|
|
113
|
+
Note over Effect: Log: "C = 3"
|
|
114
|
+
deactivate Effect
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Key Insights:**
|
|
118
|
+
- States are simple: just store the value
|
|
119
|
+
- Derivations are **lazy**: they don't compute until accessed
|
|
120
|
+
- Effects are **eager**: they execute immediately on creation
|
|
121
|
+
- Dependencies are registered **during execution** through `.get(t)`
|
|
122
|
+
|
|
123
|
+
## Update Flow
|
|
124
|
+
|
|
125
|
+
Now let's see what happens when we update `$a`:
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
$a.set(5)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### The Push-Pull Model
|
|
132
|
+
|
|
133
|
+
PicoFlow uses a "**push notification, pull computation**" model:
|
|
134
|
+
|
|
135
|
+
- **Push**: When a value changes, notifications are pushed to all dependents immediately
|
|
136
|
+
- **Pull**: Actual recomputation only happens when a value is accessed (pulled)
|
|
137
|
+
|
|
138
|
+
This is efficient because:
|
|
139
|
+
- Derivations that aren't currently needed don't recompute
|
|
140
|
+
- Multiple state changes can batch before triggering computation
|
|
141
|
+
- Only the necessary chain of dependencies is recomputed
|
|
142
|
+
|
|
143
|
+
### Step-by-Step
|
|
144
|
+
|
|
145
|
+
1. **`$a.set(5)` is called**
|
|
146
|
+
- `$a` checks if the value changed: `5 !== 1` ✓
|
|
147
|
+
- `$a` updates its internal value: `1 → 5`
|
|
148
|
+
- `$a` notifies all its dependents
|
|
149
|
+
|
|
150
|
+
2. **`$a` notifies its dependents**
|
|
151
|
+
- `$a` sends notifications to all listeners (includes `$c`)
|
|
152
|
+
- `$c` receives the notification
|
|
153
|
+
|
|
154
|
+
3. **`$c` receives notification**
|
|
155
|
+
- `$c` marks itself as needing recomputation
|
|
156
|
+
- **Does NOT recompute yet!** (lazy evaluation)
|
|
157
|
+
- `$c` propagates the notification to its dependents
|
|
158
|
+
|
|
159
|
+
4. **`$c` notifies its dependents**
|
|
160
|
+
- `$c` sends notifications to all effects (includes our effect)
|
|
161
|
+
- The effect is scheduled to execute
|
|
162
|
+
|
|
163
|
+
5. **Effect executes**
|
|
164
|
+
- Effect runs its function with tracking context
|
|
165
|
+
- Calls `$c.get(t)`
|
|
166
|
+
|
|
167
|
+
6. **`$c` recomputes (pull)**
|
|
168
|
+
- `$c.get(t)` detects that recomputation is needed
|
|
169
|
+
- Clears old dependencies
|
|
170
|
+
- Runs compute function again
|
|
171
|
+
- Compute calls `$a.get(t)` → returns `5`
|
|
172
|
+
- Compute calls `$b.get(t)` → returns `2`
|
|
173
|
+
- Computes new value: `5 + 2 = 7`
|
|
174
|
+
- Re-registers dependencies
|
|
175
|
+
- Returns `7`
|
|
176
|
+
|
|
177
|
+
7. **Effect logs the result**
|
|
178
|
+
- Effect receives `7` from `$c.get(t)`
|
|
179
|
+
- Logs: `"C = 7"`
|
|
180
|
+
|
|
181
|
+
### Update Sequence Diagram
|
|
182
|
+
|
|
183
|
+
```mermaid
|
|
184
|
+
sequenceDiagram
|
|
185
|
+
participant User
|
|
186
|
+
participant $a as $a (State)
|
|
187
|
+
participant $c as $c (Derivation)
|
|
188
|
+
participant Effect
|
|
189
|
+
|
|
190
|
+
User->>$a: set(5)
|
|
191
|
+
activate $a
|
|
192
|
+
Note over $a: Check: 5 !== 1 ✓<br/>Update: 1 → 5
|
|
193
|
+
|
|
194
|
+
$a->>$c: notify
|
|
195
|
+
activate $c
|
|
196
|
+
Note over $c: Mark as needs<br/>recomputation<br/>NO recompute yet!
|
|
197
|
+
|
|
198
|
+
$c->>Effect: notify
|
|
199
|
+
deactivate $c
|
|
200
|
+
deactivate $a
|
|
201
|
+
|
|
202
|
+
activate Effect
|
|
203
|
+
Note over Effect: Execute function
|
|
204
|
+
|
|
205
|
+
Effect->>$c: get(t)
|
|
206
|
+
activate $c
|
|
207
|
+
Note over $c: Recomputation<br/>needed? YES
|
|
208
|
+
|
|
209
|
+
Note over $c: Recompute
|
|
210
|
+
activate $c
|
|
211
|
+
Note over $c: Clear old deps
|
|
212
|
+
|
|
213
|
+
$c->>$a: get(t)
|
|
214
|
+
activate $a
|
|
215
|
+
$a-->>$c: 5
|
|
216
|
+
deactivate $a
|
|
217
|
+
|
|
218
|
+
$c->>$b: get(t)
|
|
219
|
+
activate $b
|
|
220
|
+
$b-->>$c: 2
|
|
221
|
+
deactivate $b
|
|
222
|
+
|
|
223
|
+
Note over $c: Compute: 5 + 2 = 7<br/>Re-register deps
|
|
224
|
+
deactivate $c
|
|
225
|
+
|
|
226
|
+
$c-->>Effect: 7
|
|
227
|
+
deactivate $c
|
|
228
|
+
|
|
229
|
+
Note over Effect: Log: "C = 7"
|
|
230
|
+
deactivate Effect
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**Key Insights:**
|
|
234
|
+
- **Notifications are pushed** immediately when state changes
|
|
235
|
+
- **Derivations mark as dirty** but don't recompute until needed
|
|
236
|
+
- **Effects execute immediately** when notified
|
|
237
|
+
- **Recomputation is pulled** when effects access derivations
|
|
238
|
+
- **Dependencies are re-registered** on each execution (dynamic tracking)
|
|
239
|
+
|
|
240
|
+
## Disposal Flow
|
|
241
|
+
|
|
242
|
+
Finally, let's see what happens when we dispose a primitive:
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
$a.dispose()
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Step-by-Step
|
|
249
|
+
|
|
250
|
+
1. **`$a.dispose()` is called**
|
|
251
|
+
- `$a` checks if already disposed (throws if yes)
|
|
252
|
+
- Iterates through all listeners (dependents like `$c`)
|
|
253
|
+
- Iterates through all effects (none in this case)
|
|
254
|
+
|
|
255
|
+
2. **Cleanup listeners**
|
|
256
|
+
- For each listener, calls `listener.dispose()`
|
|
257
|
+
- This triggers `$c.dispose()`
|
|
258
|
+
|
|
259
|
+
3. **`$c.dispose()` cascades**
|
|
260
|
+
- `$c` iterates through its listeners (none)
|
|
261
|
+
- `$c` iterates through its effects (includes our effect)
|
|
262
|
+
- Calls `effect.dispose()`
|
|
263
|
+
|
|
264
|
+
4. **Effect cleanup**
|
|
265
|
+
- Effect unregisters from all its dependencies (`$c`)
|
|
266
|
+
- Effect marks itself as disposed
|
|
267
|
+
- Future notifications won't trigger this effect
|
|
268
|
+
|
|
269
|
+
5. **State cleanup**
|
|
270
|
+
- `$a` unregisters from all its listeners
|
|
271
|
+
- `$a` marks itself as disposed
|
|
272
|
+
- Future attempts to use `$a` will throw errors
|
|
273
|
+
|
|
274
|
+
6. **Result**
|
|
275
|
+
- The entire dependency graph is cleaned up
|
|
276
|
+
- Memory is freed
|
|
277
|
+
- No more reactions will occur
|
|
278
|
+
|
|
279
|
+
### Disposal Sequence Diagram
|
|
280
|
+
|
|
281
|
+
```mermaid
|
|
282
|
+
sequenceDiagram
|
|
283
|
+
participant User
|
|
284
|
+
participant $a as $a (State)
|
|
285
|
+
participant $c as $c (Derivation)
|
|
286
|
+
participant Effect
|
|
287
|
+
|
|
288
|
+
User->>$a: dispose()
|
|
289
|
+
activate $a
|
|
290
|
+
Note over $a: Check if disposed<br/>Iterate listeners
|
|
291
|
+
|
|
292
|
+
$a->>$c: dispose()
|
|
293
|
+
activate $c
|
|
294
|
+
Note over $c: Iterate effects
|
|
295
|
+
|
|
296
|
+
$c->>Effect: dispose()
|
|
297
|
+
activate Effect
|
|
298
|
+
|
|
299
|
+
Effect->>$c: Unregister
|
|
300
|
+
Note over Effect,$c: Remove dependency
|
|
301
|
+
|
|
302
|
+
Note over Effect: Mark disposed<br/>Stop running
|
|
303
|
+
deactivate Effect
|
|
304
|
+
|
|
305
|
+
$c->>$a: Unregister
|
|
306
|
+
Note over $c,$a: Remove dependency
|
|
307
|
+
|
|
308
|
+
Note over $c: Mark disposed
|
|
309
|
+
deactivate $c
|
|
310
|
+
|
|
311
|
+
Note over $a: Mark disposed<br/>Clean up listeners
|
|
312
|
+
deactivate $a
|
|
313
|
+
|
|
314
|
+
Note over User,Effect: All cleaned up!<br/>Memory freed
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
**Key Insights:**
|
|
318
|
+
- **Disposal cascades** through the dependency graph
|
|
319
|
+
- **Dependents are disposed first** (effects before derivations before states)
|
|
320
|
+
- **All registrations are cleaned up** to prevent memory leaks
|
|
321
|
+
- **Disposed primitives throw errors** if you try to use them
|
|
322
|
+
- **Order matters**: dispose dependents before dependencies when doing manual cleanup
|
|
323
|
+
|
|
324
|
+
## Practical Implications
|
|
325
|
+
|
|
326
|
+
Understanding these flows helps you:
|
|
327
|
+
|
|
328
|
+
### 1. Optimize Performance
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
// Derivation won't compute if effect is disposed
|
|
332
|
+
const $expensive = derivation((t) => {
|
|
333
|
+
return expensiveCalculation($data.get(t))
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
const fx = effect((t) => {
|
|
337
|
+
if (shouldDisplay) {
|
|
338
|
+
display($expensive.get(t)) // Only computes when accessed
|
|
339
|
+
}
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
// When not needed, just dispose the effect
|
|
343
|
+
fx.dispose() // $expensive stops computing
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### 2. Debug Dependency Issues
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
// If logs don't appear, check:
|
|
350
|
+
// 1. Is the effect created? (runs immediately)
|
|
351
|
+
// 2. Are you using .get(t)? (not .pick())
|
|
352
|
+
// 3. Is anything disposed?
|
|
353
|
+
|
|
354
|
+
effect((t) => {
|
|
355
|
+
console.log('Effect created') // Should log immediately
|
|
356
|
+
console.log('Value:', $state.get(t)) // Creates dependency
|
|
357
|
+
})
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### 3. Avoid Memory Leaks
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
// Always dispose effects when done
|
|
364
|
+
const disposables: FlowDisposable[] = []
|
|
365
|
+
|
|
366
|
+
disposables.push(effect((t) => { /* ... */ }))
|
|
367
|
+
disposables.push(effect((t) => { /* ... */ }))
|
|
368
|
+
|
|
369
|
+
// Clean up
|
|
370
|
+
disposables.forEach(d => d.dispose())
|
|
371
|
+
```
|