kask kask - 3 months ago 43
Javascript Question

rxjs map operator evaluated for each subscriber

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?

Answer

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.