Subscription
Leaf's follow RxJS' Observable pattern; you subscribe to their value and receive updates via the listener:
const numLeaf = new Forest(6);
numLeaf.value = 7;
const sub = numLeaf.subscribe((value) => console.log('number is now', value));
numLeaf.value = 8;
numLeaf.value = 9;
numLeaf.value = 10;
sub.unsubscribe();
numLeaf.value = 11;
numLeaf.value = 12;
/**
'number is now', 7
'number is now', 8
'number is now', 9
'number is now', 10
*/
Subscribe returns a Subscriber which can be deactivated with .unsubscrube()
, as shown above.
subscribe(listener)
also accepts the tri-fold listener as it is a delegation to an 'Rxjs Subject` (opens in a new tab):
const sub = myLeaf.subscribe({
next(value) {
console.log(value);
},
complete() {
console.log('done')
}
});
// .... some time in the future
sub.unsubscribe();
Transactions
All actions and updates are contained within transactions. Transactional activity muffles notification to
subscribers. so, for instance, you can write an action and call three or four myLeaf.setX(2); myLeaf.setY(3)
calls,
and only one update will be emitted.
Even though the changes are actually occurring synchronously, and parent leafs are getting updated, you won't see change in a listener until the outermost transaction completes.
If you were to do that outside an action, several updates will be broadcast. Also, any activity in a transaction with a thrown error is flushed completely including changes that occurred before the error.
const leaf = new Forest(
{
$value: {
x: 0,
y: 0,
z: 0
},
actions: {
updateAll(leaf, x, y, z) {
leaf.do.set_x(x);
leaf.do.set_y(y);
console.log('leafs y is now ', leaf.value.y);
leaf.do.set_z(z);
if (typeof y !== 'number') throw new Error('Y has a bad value');
},
},
}
);
leaf.subscribe(value => console.log(value));
leaf.do.set_x(1);
leaf.do.set_y(2);
leaf.do.set_z(3);
leaf.do.updateAll(4, 5, 6);
console.log('---- bad value ----')
try {
leaf.do.updateAll(7, '8', 9);
} catch (err) {
console.log('error:', err);
}
console.log('current value of leaf: ', leaf.value);
/**
{ x: 0, y: 0, z: 0 }
{ x: 1, y: 0, z: 0 }
{ x: 1, y: 2, z: 0 }
{ x: 1, y: 2, z: 3 }
leafs y is now 5
{ x: 4, y: 5, z: 6 }
---- bad value ----
leafs y is now 8
error: Error: Y has a bad value ...
...
current value of leaf: { x: 4, y: 5, z: 6 }
*/
The same code in action updateAll()
causes a single update where, if called outside an action, it broadcasts three
distinct updates. Also even though inside the updateAll leaf is changed to '8', the error thrown after all the
values have been 'changed' revert.
Every action in a leaf (including setters) takes a "snapshot" of values before they change; on any thrown error, the value is reset to that value if an error is thrown. This insures that changes no matter how nested either all succeed or all fail.