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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
//! An implementation of a fine-grained reactive system.
//!
//! Fine-grained reactivity is an approach to modeling the flow of data through an interactive
//! application by composing together three categories of reactive primitives:
//! 1. **Signals**: atomic units of state, which can be directly mutated.
//! 2. **Computations**: derived values, which cannot be mutated directly but update whenever the signals
//! they depend on change. These include both synchronous and asynchronous derived values.
//! 3. **Effects**: side effects that synchronize the reactive system with the non-reactive world
//! outside it.
//!
//! Signals and computations are "source" nodes in the reactive graph, because an observer can
//! subscribe to them to respond to changes in their values. Effects and computations are "subscriber"
//! nodes, because they can listen to changes in other values.
//!
//! ```rust
//! # any_spawner::Executor::init_futures_executor();
//! # let owner = reactive_graph::owner::Owner::new(); owner.set();
//! use reactive_graph::{
//! computed::ArcMemo,
//! effect::Effect,
//! prelude::{Read, Set},
//! signal::ArcRwSignal,
//! };
//!
//! let count = ArcRwSignal::new(1);
//! let double_count = ArcMemo::new({
//! let count = count.clone();
//! move |_| *count.read() * 2
//! });
//!
//! // the effect will run once initially
//! Effect::new(move |_| {
//! println!("double_count = {}", *double_count.read());
//! });
//!
//! // updating `count` will propagate changes to the dependencies,
//! // causing the effect to run again
//! count.set(2);
//! ```
//!
//! This reactivity is called "fine grained" because updating the value of a signal only affects
//! the effects and computations that depend on its value, without requiring any diffing or update
//! calculations for other values.
//!
//! This model is especially suitable for building user interfaces, i.e., long-lived systems in
//! which changes can begin from many different entry points. It is not particularly useful in
//! "run-once" programs like a CLI.
//!
//! ## Design Principles and Assumptions
//! - **Effects are expensive.** The library is built on the assumption that the side effects
//! (making a network request, rendering something to the DOM, writing to disk) are orders of
//! magnitude more expensive than propagating signal updates. As a result, the algorithm is
//! designed to avoid re-running side effects unnecessarily, and is willing to sacrifice a small
//! amount of raw update speed to that goal.
//! - **Automatic dependency tracking.** Dependencies are not specified as a compile-time list, but
//! tracked at runtime. This in turn enables **dynamic dependency tracking**: subscribers
//! unsubscribe from their sources between runs, which means that a subscriber that contains a
//! condition branch will not re-run when dependencies update that are only used in the inactive
//! branch.
//! - **Asynchronous effect scheduling.** Effects are spawned as asynchronous tasks. This means
//! that while updating a signal will immediately update its value, effects that depend on it
//! will not run until the next "tick" of the async runtime. (This in turn means that the
//! reactive system is *async runtime agnostic*: it can be used in the browser with
//! `wasm-bindgen-futures`, in a native binary with `tokio`, in a GTK application with `glib`,
//! etc.)
//!
//! The reactive-graph algorithm used in this crate is based on that of
//! [Reactively](https://github.com/modderme123/reactively), as described
//! [in this article](https://dev.to/modderme123/super-charging-fine-grained-reactive-performance-47ph).
use ;
pub
use ScopedFuture;
/// Reexports frequently-used traits.
// TODO remove this, it's just useful while developing
/// Calls [`Executor::spawn`](any_spawner::Executor::spawn) on non-wasm targets and [`Executor::spawn_local`](any_spawner::Executor::spawn_local) on wasm targets, but ensures that the task also runs in the current arena, if
/// multithreaded arena sandboxing is enabled.
/// Calls [`Executor::spawn_local`](any_spawner::Executor::spawn_local), but ensures that the task also runs in the current arena, if
/// multithreaded arena sandboxing is enabled.
/// Calls [`Executor::spawn_local`](any_spawner::Executor), but ensures that the task runs under the current reactive [`Owner`](crate::owner::Owner) and observer.
///
/// Does not cancel the task if the owner is cleaned up.
/// Calls [`Executor::spawn_local`](any_spawner::Executor), but ensures that the task runs under the current reactive [`Owner`](crate::owner::Owner) and observer.
///
/// Cancels the task if the owner is cleaned up.