如何使用rxjs存储扫描的累积结果

时间:2015-05-31 23:12:26

标签: javascript reactive-programming rxjs reactive-extensions-js

合并后我有两个合并的observable和一个扫描。第一个是简单范围,另一个是主题。每当Subject使用onNext发出一个新值时,我会在扫描中连接该值并将新数组作为累加器返回。如果我处理了我的订阅,然后再次订阅,它会重放该范围内的值,但我丢失了主题中的值。在下面的代码中,我希望我的第二个订阅的最终值为[1, 2, 3, 4, 5]

最好的方法是什么?现在我有另一个主题,我存储了最终值并订阅了它,但它感觉错误。

这是一个简单的版本,演示了正在发生的事情:

var Rx = require('rx');

var source = Rx.Observable.range(1, 3);

var adder = new Rx.Subject();

var merged = source.merge(adder)
                    .scan([], function(accum, x) {
                        return accum.concat(x);
                    });

var subscription1 = merged.subscribe(function(x) {console.log(x)});
adder.onNext(4);
adder.onNext(5);

subscription1.dispose();

console.log('After Disposal');

var subscription2 = merged.subscribe(function(x) {console.log(x)});

输出:

[ 1 ]
[ 1, 2 ]
[ 1, 2, 3 ]
[ 1, 2, 3, 4 ]
[ 1, 2, 3, 4, 5 ]
After Disposal
[ 1 ]
[ 1, 2 ]
[ 1, 2, 3 ]

2 个答案:

答案 0 :(得分:7)

主题是一个热门的观察者,这就是为什么第二个订阅不会看到来自主题的事件。 Observable范围很冷,所以每个"执行实例"完全归每个订阅所有。另一方面,主题"执行实例"是单身且独立的,因此第二个订阅没有看到事件。

有几种方法可以解决这个问题。

  1. 使用ReplaySubject。您必须指定缓冲区大小。如果您对该缓冲区没有良好的限制,使用无限制的缓冲区可能会导致内存问题。
  2. 避免使用主题。换句话说,避免使用热Observable,用冷Observable替换它,并根据我在开头给出的问题描述,你的订阅不会有问题,他们会看到相同的事件。通常,Subject可以被Observable替换,但这取决于您的整体架构。可能需要重写很多。在最坏的情况下,例如Observables的循环依赖,你无法避免使用Subject。
  3. 重新排列代码,使订阅在主题开始发布之前开始,因此所有订阅都有机会看到" live"来自热的观察者的排放。
  4. 但是,如果我对此问题的解释是正确的,那么您只需要merged发出的最后一个事件,因此您可以使用替代(1)的变体,其中您只重放最后一个事件。这是将.shareReplay(1)添加到merged的问题,这将使其成为热门的重播Observable。

答案 1 :(得分:1)

根据您的说明,您似乎正在寻找BehaviorSubjectReplaySubject(或其相应的运算符,publishValue()replay()),而不是{{1} }。

每次重新访问页面时,您都可以使用以下方法来连接状态,(因为您提到它我将使用todo应用程序示例):

scan

这是一个工作代码示例我认为镜像你正在寻找的东西,注意在这种情况下我不需要扫描,因为列表被链条上的var todos = buttonClicked .map(function(e) { return newTodoBox.text(); }) //This will preserve the state of the todo list across subscriptions .replay(); var todoSubscription; pageActivated.subscribe(function() { todoSubscription = new Rx.CompositeDisposable( //Add the items to the list todos.subscribe(addItem), //This enables the replay to actually start //receiving values todos.connect()); /*Do other activation work*/ }); pageDeactivated.subscribe(function() { todoSubscription && todoSubscription.dispose(); /*Do other clean up work*/ }); 完全重新水化。您会注意到,如果您添加了一些待办事项,那么请断开连接并再次连接,这些项目将重新出现。

replay()
var buttonClicked = Rx.Observable.fromEvent($('#add'), 'click');

var disconnect = Rx.Observable.fromEvent($('#disconnect'), 'click');

var connect = Rx.Observable.fromEvent($('#connect'), 'click');

var newTodoBox = $('#todos')[0];

var mylist = $('#mylist');

function addItem(e) {
  mylist.append('<li>' + e + '</li>');
}

var todos = buttonClicked
  .map(function(e) {
    return newTodoBox.value;
  })
  .replay();

var todoSubscription;

disconnect.subscribe(function() {
  mylist.empty();
  todoSubscription && todoSubscription.dispose();
});

connect.startWith(true).subscribe(function() {
  todoSubscription = new Rx.CompositeDisposable(
    todos.subscribe(addItem),
    todos.connect());
});