简体   繁体   中英

Rx.js fromEvent + flatMapLatest broken?

Well, the problem itself is kind of hard to describe briefly, so here's a live example to demonstrate. It seems like I'm misunderstanding something about how Rx.js works, otherwise the functionality here comes from a bug.

What I tried to do was a simple reactive rendering setup, where what you see on the screen, and what events happen are both described in terms of Observables. The problem is that, for some indiscernible reason, the events are dropped entirely when the code is written one way, yet work fine with code that should theoretically be equivalent.

So, let's start with the first case in the example code above:

var dom = makeBox('one');
var clicks = Rx.Observable.fromEvent(dom, 'click');

If you create a DOM fragment, then you can simply use fromEvent to get an Observable for whatever event it emits. So far, so good. You can click this box and see a bunch of lines written to the log.

Now, the next step would be to make the DOM reactive, to express how it changes over time.

var domStream = Rx.Observable.return(makeBox('two'));
var clicks = domStream.flatMapLatest(function(dom) {
  return Rx.Observable.fromEvent(dom, 'click');
});

That would make it an Observable, using return here to produce the simplest, constant case. The events you're interested in would be the ones emitted by the latest version of the dom, and that's exactly what the flatMapLatest operator does. This variant still works.

Ultimately, the goal would be to generate the current DOM state based on some application state. That is, map it from one Observable to another. Let's go with the simplest version for now, have a single constant value as the state, and then map it to the same fixed output we used previously:

var updates = Rx.Observable.return(1);
var domStream = updates.map(function (update) {
  return makeBox('three');
});
var clicks = domStream.flatMapLatest(function(dom) {
  return Rx.Observable.fromEvent(dom, 'click');
});

This should not be any different from the previous version. However, this outputs no events, no matter what you do.

What exactly is going on here? Did I misunderstand some fundamental concept of Rx, or what? I've run into some issues with hot vs cold Observables, but that seems unrelated in this minimal case. So, I'm kind of out of ideas. Can anyone enlighten me?

Sorry to tell you but it is a Hot vs Cold issue.

It is a subtle issue, but the difference between

Rx.Observable.return(makeBox('two'))

and

Rx.Observable.return(1).map(function() {return makeBox('three'); })

Is that the first returns a constant every time you subscribe to it, that is, a box that you created initially. The second returns a new box every time the Observable is subscribed to, this causes a problem since you actually subscribe to the domStream variable twice, you are creating two instances of Box three, one which has event handlers but isn't shown and one that does not and is shown.

The fix is that you either need to use approach 2 or you need to convert the third into a hot stream either by using:

domStream.replay(1).refCount()

Or by using

domStream.publish()

then after all subscriptions are completed:

domStream.connect()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM