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
176 | /**
* SPDX-FileCopyrightText: 2026 Maximiliano Ramirez <maximiliano.ramirezbravo@gmail.com>
*
* SPDX-License-Identifier: MIT
*/
/**
* ReactiveESP32 Example Overview:
* - This example demonstrates batch updates when changing multiple signals to avoid redundant
* computations and side effects.
* - When multiple Signals change, dependents normally run after each change.
* - freeze() pauses reactive updates, allowing multiple changes without triggering Effects.
* - unfreeze() resumes updates and triggers dependents once with all accumulated changes.
* - This is useful for atomic updates and performance optimization.
*
* - We create three Signals (x, y, z) and a Computed that sums them.
* - An Effect prints the sum.
* - With freeze/unfreeze, the Effect runs once instead of three times.
*
* - The Serial interface is used to read commands:
* - 'g': Get current values.
* - 'u': Update all three values individually (3 Computed/Effect runs).
* - 'b': Batch update using freeze/unfreeze (1 Computed/Effect run).
* - 'r': Reset all to zero using batch helper.
*
* - Pressing '0' restarts the ESP32.
*/
#include <ReactiveESP32.h>
using namespace RxESP32;
/* ---------------------------------------------------------------------------------------------- */
// Three signals
Signal<int> x(0, {.name = "x"});
Signal<int> y(0, {.name = "y"});
Signal<int> z(0, {.name = "z"});
// Computed sum
Computed<int> sum(
[]() {
int result = x.get() + y.get() + z.get();
Serial.printf("\t[Computed] Calculating sum: %d + %d + %d = %d\n",
x.get(),
y.get(),
z.get(),
result);
return result;
},
{.name = "sum"});
// Effect to print the sum
Effect<> print_sum(
[]() {
static int count = 0;
count++;
int result = sum.get();
Serial.printf("\t[Effect #%d] Sum changed to: %d\n", count, result);
return nullptr; // No cleanup function
},
{.name = "print_sum"});
// Read Serial input and process commands
void serialRead();
/* ---------------------------------------------------------------------------------------------- */
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("====================================");
Serial.println("ReactiveESP32 - BatchUpdates Example");
Serial.println("====================================");
// Start the ReactiveESP32 dispatcher
if (!Dispatcher::start()) {
Serial.println("Failed to start ReactiveESP32 Dispatcher!");
while (true) {
delay(1000);
}
}
}
void loop() { serialRead(); }
void serialRead() {
if (!Serial.available()) return;
char c = Serial.read();
if (c == '\r') return;
if (c == '\n') c = ' ';
Serial.printf("> %c\n", c);
switch (c) {
case '0':
{
ESP.restart();
} break;
case 'g':
{
// Get current values
Serial.printf("x=%d, y=%d, z=%d, sum=%d\n", x.get(), y.get(), z.get(), sum.get());
} break;
case 'u':
{
// Update individually
Serial.println("[Individual] Updating individually (3 separate changes):");
// After each update, add a small delay to ensure dispatcher processes updates
// Otherwise, first update would mark the "sum" as dirty, and subsequent updates would do
// nothing since "sum" is already dirty.
x.update([](const int& curr) { return curr + 1; });
delay(5); // Allow dispatcher to process
y.update([](const int& curr) { return curr + 2; });
delay(5); // Allow dispatcher to process
z.update([](const int& curr) { return curr + 3; });
delay(5); // Allow dispatcher to process
Serial.println("[Individual] Done\n");
} break;
case 'b':
{
// Batch update using freeze/unfreeze
Serial.println("[Batch] Freezing signals:");
// Freeze all signals
x.freeze();
y.freeze();
z.freeze();
Serial.println("[Batch] Updating values while frozen:");
x.update([](const int& curr) { return curr + 1; });
delay(5); // Not necessary, just to compare with individual case
y.update([](const int& curr) { return curr + 2; });
delay(5); // Not necessary, just to compare with individual case
z.update([](const int& curr) { return curr + 3; });
delay(5); // Not necessary, just to compare with individual case
// Unfreeze all signals
Serial.println("[Batch] Unfreezing signals:");
// Manually set should_notify parameter to true on the last unfreeze to trigger updates once
x.unfreeze(false); // Should not notify yet
y.unfreeze(false); // Should not notify yet
z.unfreeze(true); // Notify now
delay(10);
Serial.println("[Batch] Done - Effect ran only once");
} break;
case 'r':
{
// Reset values using batch helper
Serial.println("Resetting all values to 0");
Helpers::Utility::batch(
[]() {
x.set(0);
y.set(0);
z.set(0);
},
x,
y,
z); // "z" will notify after unfreeze
} break;
}
}
|