Reputation: 1759
Why is the map operator evaluated for each subscriber instead of once?
const obs1 = Rx.Observable.interval(1000).take(1).map((x, i) => {
console.log(i+1 + ':1 map')
return 'obs1';
})
const obs2 = Rx.Observable.interval(1300).take(1).map((x, i) => {
console.log(i+1 + ':2 map')
return 'obs2';
})
const obs3 = Rx.Observable.interval(1700).take(2).map((x, i) => {
console.log(i+1 + ':3 map')
return 'obs3';
})
const view = obs1.combineLatest(obs2, obs3, (obs1, obs2, obs3) => { return obs1 + ', ' + obs2 + ', ' + obs3; });
// Every subscriber adds more calls to map - why is it called multiple times at the same time ?
view.subscribe((value) => {
console.log('sub1: ' + value)
});
view.subscribe((value) => {
console.log('sub2: ' + value)
});
view.subscribe((value) => {
console.log('sub3: ' + value)
});
I created a testcase here: http://jsbin.com/jubinuf/3/edit?js,console
Can I write this testcase differently to avoid this behaviour?
Upvotes: 6
Views: 752
Reputation: 14144
Every subscriber will run through the Observable sequence. If you want everyone to get the resulting stream instead, use .publish().refCount()
.
http://jsbin.com/ditikonopi/edit?js,console
The .publish()
will return an observable sequence that shares a single subscription to the underlying sequence. refCount()
will stay connected to the source so long as there is at least one subscription.
Upvotes: 6
Reputation: 18665
Kyle answer is correct. publish().refCount()
applied to all three observables will result in the map
selector function not being reexecuted.
To elaborate a bit more on that answer, it is useful to understand the difference between hot and cold observables when you use Rxjs. In your case, all your observables obsX
are cold, so they are 'restarted' on subscription. combineLatest
subscribes under the hood to all 3, which is why map
is reexecuted. Have a look here for an illustrated explanation. It is a common stumbling block for beginners but it is quite simple to understand.
Upvotes: 4