proposal-signals
proposal-signals copied to clipboard
How to allow `set` in a `Computed` to trigger a recomputation synchronously (until the result is stable)
Hello, As a maintainer of the tansu signal library, I am trying (here) to re-implement it using signal-polyfill and I came across the following difference of behavior, that I would like to solve, avoiding any breaking change.
With tansu
, calling set
on a signal in a computed
that has a dependency on that signal re-triggers the computation of the computed until the value is stable (with a fixed maximum number of iterations). For example:
import {writable, computed} from "@amadeus-it-group/tansu";
const s = writable(0);
const c = computed(() => {
const value = s();
if (value < 10) {
s.set(value + 1);
}
return value;
});
const d = computed(() => {
const value = s();
if (value < 10) {
s.set(value + 1);
}
return value;
});
console.log(c()); // logs: 10
console.log(c()); // logs: 10
console.log(d()); // logs: 10
console.log(d()); // logs: 10
console.log(c()); // logs: 10
console.log(d()); // logs: 10
(cf this test)
With signal-polyfill
, there was apparently a different design decision:
import { Signal } from 'signal-polyfill';
const s = new Signal.State(0);
const c = new Signal.Computed(() => {
const value = s.get();
if (value < 10) {
s.set(value + 1);
}
return value;
});
const d = new Signal.Computed(() => {
const value = s.get();
if (value < 10) {
s.set(value + 1);
}
return value;
});
console.log(c.get()); // logs: 0
console.log(c.get()); // logs: 0
console.log(d.get()); // logs: 1
console.log(d.get()); // logs: 1
console.log(c.get()); // logs: 2
console.log(d.get()); // logs: 3
My question is simple: how may I reproduce the behavior of tansu
when implementing it using signal-polyfill
?
Do you think this signals proposal could change to adopt this different behavior?
Alternatively, we could probably implement this if we had some other primitives that are missing in the current specification (and I think those would be useful anyway):
- having a way to intercept tracked reads (as suggested by @shaylew for a different issue here)
- having a way to know whether a computed signal is dirty (i.e. one of its direct or transitive dependencies have changed since the last computation)
- having a way to know whether a computed signal actually changed after recomputing it (taking into account its own equals function)
This way, maybe we could have a loop in our tansu
computed
which, after each computation, goes over all tracked reads called during the computed and checks if any of them is dirty at the end of computed. If it is the case, it means one of the (direct or transitive) dependencies changed during the call to computed and that we may need to recompute (in case those dependencies really changed).
What do you think?