RxJS扫描和组合行为

时间:2015-09-01 11:34:48

标签: javascript reactive-programming rxjs

我一直在学习RxJs,我在尝试确定使用scan和combinelatest时看到的输出背后的逻辑时遇到了问题

我有这个示例应用程序



var subject = new Rx.Subject();

var stream = subject.map(function(x) {
  return function(v) {
    return v + " stream";
  };
});

var state = Rx.Observable.merge(stream)
  .startWith("StartWith")
  .scan(function(x, f) {
    console.log("scan");
    return f(x);
  });

var view1 = state.map(function(x) {
  return "view1 " + x;
});

var view2 = state.map(function(x) {
  return " view2 " + x;
});

Rx.Observable.combineLatest(view1, view2, function(x, y) {
  return [x, y].join(",");
}).subscribe(function(x) {
  console.log(x);
});

subject.onNext("Once");
subject.onNext("Twice");




当您查看应用程序的控制台日志时,它会输出以下内容

LOG: view1 StartWith, view2 StartWith, 
LOG: scan 
LOG: view1 StartWith stream, view2 StartWith, 
LOG: scan 
LOG: view1 StartWith stream, view2 StartWith stream, 
LOG: scan 
LOG: view1 StartWith stream stream, view2 StartWith stream, 
LOG: scan 
LOG: view1 StartWith stream stream, view2 StartWith stream stream, 

我不明白为什么经常调用扫描。我只是为主题调用了两次,但似乎是4次调用扫描功能。如果我添加更多视图,它将再添加2个扫描调用,依此类推。

任何人都可以解释为什么会这样吗?

1 个答案:

答案 0 :(得分:7)

因为你订阅了两次。 CombineLatest将隐式订阅您传递的两个流。当您第一次使用Rx时,这是非常常见的错误,除非运营商声称为每个订阅共享基础可观察量,否则每个订阅都会创建一个可观察流。

也就是说,上面的代码在功能上与以下代码相当(但实际上并没有这样做):

var subject = new Rx.Subject();

var source1 = subject
  .map(function(x) {
    return function(v) {
      return v + " stream";
    };
  })
  .startWith("StartWith")
  .scan(function(x, f) {
    console.log("scan");
    return f(x);
  }).map(function(x) {
    return "view1 " + x;
  });

var source2 = subject
  .map(function(x) {
    return function(v) {
      return v + " stream";
    };
  })
  .startWith("StartWith")
  .scan(function(x, f) {
    console.log("scan");
    return f(x);
  })
  .map(function(x) {
    return " view2 " + x;
  })


var x, y;

source1.subscribe(function(_x) { 
   x = _x;
   if (typeof y !== 'undefined') {  
     console.log([x, y].join(","));
   });

source2.subscribe(function(_y) { 
   y = _y;
   if (typeof x !== 'undefined') {
     console.log([x, y].join(","));
   });

subject.onNext("Once");
subject.onNext("Twice");

如果您希望仅扫描一次扫描,则应在scan()之后使用share(),以便Observable之间的scan将在订阅者之间共享。

所以state应该是:

var state = Rx.Observable.merge(stream)
  .startWith("StartWith")
  .scan(function(x, f) {
    console.log("scan");
    return f(x);
  })
  .share();