Rx BehaviorSubject + scan将先前事件推送给新订户?

时间:2015-12-06 05:20:58

标签: javascript rxjs

我想要一个可以推送减速器功能的流。每次按下reducer函数时,都应该将状态对象传递给reducer,reducer应返回修改后的状态值,并且应该将更新后的状态推送给订阅者。我希望我的代码可以解释:

import Rx from 'rx';
import { Map } from 'immutable';

let initialState = Map({ counter: 0 });

export let upstream = new Rx.BehaviorSubject(Rx.helpers.identity);
export let downstream = upstream.scan((state, reducer) => {
  return reducer(state);
}, initialState);

let increment = state => {
  return state.update('counter', counter => counter + 1);
};

upstream.onNext(increment);

downstream.subscribe(state => {
  console.log('subscriptionA', state.get('counter'));
});

upstream.onNext(increment);

setTimeout(() => {
  downstream.subscribe(state => {
    console.log('subscriptionB', state.get('counter'));
  });
}, 3000);

这是我看到的输出:

subscriptionA 1
subscriptionA 2
subscriptionB 1

虽然我希望看到:

subscriptionA 1
subscriptionA 2
subscriptionB 2

显然我在这里遗漏了一些基本的东西。似乎BehaviorSubject应该保留新订阅者的最新价值,这会让我认为当subscriptionB订阅downstream时它会获得最新的减少价值,但它看起来像中间有.scan犯规的东西......或其他东西。

这里发生了什么以及如何实现我想要完成的目标?谢谢!

2 个答案:

答案 0 :(得分:3)

如果您更换

,您是否可以尝试查看一切是否符合您的期望?
export let downstream = upstream.scan((state, reducer) => {
  return reducer(state);
}, initialState);

通过

export let downstream = upstream.scan((state, reducer) => {
  return reducer(state);
}, initialState).shareReplay(1);

jsfiddle here:http://jsfiddle.net/cqaumutp/

如果是这样,你是Rx.Observable的热与冷性质的另一个受害者,或者更准确地说是可观察者的懒惰实例化。

简而言之(并非如此简短),每次执行subscribe时会发生什么,是通过向运营商链上游创建一系列可观察量。每个运算符都订阅其源,并将另一个observable返回到起始源。在您的情况下,当您订阅scan时,scan订阅upstream,这是最后一个。 upstream作为主题,在订阅时它只注册订阅者。其他来源可以做其他事情(比如在DOM节点或套接字上注册监听器,或其他任何东西)。

这里的要点是,每次订阅scan时,都会重新开始,即使用initialState。如果要使用第一次订阅scan的值,则必须使用share运算符。在第一次订阅share时,它会将您的订阅请求传递给scan。在第二个及后续的那个,它将不会,它将注册它,并转发给相关的观察者所有来自scan首先订阅的值。

答案 1 :(得分:0)

我有一个解决方案似乎正在给我我想要的结果。如果其他人能够证明它是一个合适的解决方案,我将不胜感激。

import Rx from 'rx';
import { Map } from 'immutable';

let initialState = Map({ counter: 0 });

export let upstream = new Rx.Subject();

let downstreamSource = upstream.scan((state, reducer) => {
  return reducer(state);
}, initialState);

export let downstream = new Rx.BehaviorSubject(initialState);
downstreamSource.subscribe(downstream);

let increment = state => {
  return state.update('counter', counter => counter + 1);
};

upstream.onNext(increment);

downstream.subscribe(state => {
  console.log('subscriptionA', state.get('counter'));
});

upstream.onNext(increment);

setTimeout(() => {
  downstream.subscribe(state => {
    console.log('subscriptionB', state.get('counter'));
  });
}, 3000);