I am learning angular 2 and rxjs. I have 3 variables, A B C.
- B depends on the value of A
- C depends on the value of A and B
I am trying to setup up the observables such that: When A is updated, B and C will be auto updated. When B is updated, C will be auto updated. I tried two setups but they are not satisfactory.
- First setup: B subscribes observable A; C subscribes observable B withlatestfrom A. The changes in A did cascade down to B then to C but the value from A is not the latest.
- Second setup: B subscribes observable A; C subscribes combineLatest observable of A and B. This setup works but I get two updates for C, first from B and then from A.
How can I set up my observables / subscription such that when A is updated, C will only get updated once with latest value from A and B?
EDIT - CODES ADDED
var A = new Rx.BehaviorSubject(1);
var A_Observable = A.asObservable();
var B = new Rx.BehaviorSubject(10);
var B_Observable = B.asObservable();
A_Observable.subscribe(function(A_value) {
var newB = A_value * 10;
// console.log("B auto updating to " + newB);
B.next(newB);
});
// LATEST FROM OBSERVABLE
var latestFromObservable = B_Observable.withLatestFrom(A_Observable);
latestFromObservable.subscribe(function(data) {
console.log("LATEST FROM : Value for A is " + data[1] + " ; Value for B is " + data[0]);
});
// COMBINE ALL OBSERVABLE
var combineAllObservable = Rx.Observable.combineLatest(A_Observable,B_Observable);
combineAllObservable.subscribe(function(data) {
console.log("COMBINE LATEST : Value for A is " + data[0] + " ; Value for B is " + data[1]);
});
// UPDATE TO A
setTimeout(function(){
console.log("UPDATING A");
A.next(2);
},1000);
// SATISFACTORY RESULT
setTimeout(function(){
console.log("SATISFACTORY RESULT : Value for A is 2 ; Value for B is 20 --- CALLED ONLY ONCE WITH LATEST VALUES");
},2000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.0.1/Rx.min.js"></script>
Alright, so I tested with your code. Tweak to change a few things. You can see the code here: https://jsbin.com/zonedirogi/edit?html,js,console,output. The main change is actually instead of B with latest from A, change to A with latest from B instead. Then it work as expected.
FYI, you don't need to use
asObservable()
to convert subject to observable.Code behaves this way because by default RxJs runs code synchronously. Chain of events for
withLatestFrom
case:A
value:A.next(2);
A
:B
-subscription (comes first)withLatestFrom
(comes second)B
-subscriptionB
B
:C
-subscriptionC
-subscription (with old value ofA
!)A
is pulled inwithLatestFrom
butC
-subscription isn't triggered (because it is triggered only byB
updates)Possible solution is to add
debounceTime(0)
.It will forceC
-subscription to run asynchronously when the value insidewithLatestFrom
is already updated.For more info google for RxJs's
Scheduler
.I want to add two things:
withLatestFrom
approach is better in your case because dependency graph looks likeC -> B -> A
and when A is updated B is updated too. IfA
andB
were independent thencombineLatest
would be the better choice.Subject
forB
. You can rewrite code like this (nodebounceTime
!)