As an exercise I'm trying to build 2 dependent streams which update one another.
The test application is simply an "Inches <-> Centimeters" converter, with both inputs editable.
The issue I am experiencing is that I cannot get how can I stop recursion that causes one field change.
To better explain the issue let's have a look at the relevant part of code:
var cmValue = new Rx.BehaviorSubject(0),
inValue = new Rx.BehaviorSubject(0);
# handler #1
cmValue.distinctUntilChanged().subscribe(function(v) {
inValue.onNext(cmToIn(v));
});
# handler #2
inValue.distinctUntilChanged().subscribe(function (v) {
cmValue.onNext(inToCm(v));
});
So we define to Subjects each of which holds the current corresponding value.
Now imagine we change the value in inches to 2
(using inValue.onNext(2);
or via keyboard).
What happens next - is the handler #2 is triggered and it invokes a corresponding recalculation of a value in centimeters. Which results to cmValue.onNext(0.7874015748031495)
.
This call in fact is then handled by handler #1 and causes the value in inches (the one we put manually) to be recalculated, using 0.7874015748031495 * 2.54
formula which causes another inValue.onNext(1.99999999999999973)
call.
Luckily - due to FP rounding error that's where we stop. But in other scenarios this may lead to more loops or even to an infinite recursion.
As you can see - I partially solved the issue applying .distinctUntilChanged()
which at least protects us from an infinite recursion on any change, but as we can see - in this case it's does not solve the problem entirely since values are not identical (due to FP operations nature).
So the question is: how would one implement a generic two-way binding that does not cause self-recursion at all?
I emphasized generic to make a note that using .select()
with rounding would be a partial solution for this particular issue, and not the generic one (which I and everyone else would prefer).
The complete code and demo: http://jsfiddle.net/ewr67eLr/