rxjs 5 publishReplay refCount

时间:2017-02-12 15:57:36

标签: rxjs rxjs5

我无法弄清楚publishReplay().refCount()是如何运作的。

例如(https://jsfiddle.net/7o3a45L1/):

var source = Rx.Observable.create(observer =>  {
  console.log("call"); 
  // expensive http request
  observer.next(5);
}).publishReplay().refCount();

subscription1 = source.subscribe({next: (v) => console.log('observerA: ' + v)});
subscription1.unsubscribe();
console.log(""); 

subscription2 = source.subscribe({next: (v) => console.log('observerB: ' + v)});
subscription2.unsubscribe();
console.log(""); 

subscription3 = source.subscribe({next: (v) => console.log('observerC: ' + v)});
subscription3.unsubscribe();
console.log(""); 

subscription4 = source.subscribe({next: (v) => console.log('observerD: ' + v)});
subscription4.unsubscribe();

给出以下结果:

  

致电观察员:5

     

observerB:5调用observerB:5

     

observerC:5 observerC:5 call observerC:5

     

observerD:5 observerD:5 observerD:5 call observerD:5

1)为什么观察者B,C和D被多次调用?

2)为什么在每一行打印“call”而不是在行的开头?

另外,如果我调用publishReplay(1).refCount(),它会调用observerB,C和D各2次。

我所期待的是每个新观察者只接收一次值5,而“call”只打印一次。

3 个答案:

答案 0 :(得分:23)

publishReplay(x).refCount()合并了以下内容:

  • 创建ReplaySubject,重播 x 排放。如果未定义 x ,则会重播完整的流。
  • 使用refCount()运算符使ReplaySubject多播兼容。这导致并发订阅接收相同的排放。

您的示例包含一些问题,使它们如何一起工作。请参阅以下修订的代码段:

var state = 5
var realSource = Rx.Observable.create(observer =>  {
  console.log("creating expensive HTTP-based emission"); 
  observer.next(state++);
//  observer.complete();
  
  return () => {
    console.log('unsubscribing from source')
  }
});


var source = Rx.Observable.of('')
  .do(() => console.log('stream subscribed'))
  .ignoreElements()
  .concat(realSource)
.do(null, null, () => console.log('stream completed'))
.publishReplay()
.refCount()
;
    
subscription1 = source.subscribe({next: (v) => console.log('observerA: ' + v)});
subscription1.unsubscribe();
 
subscription2 = source.subscribe(v => console.log('observerB: ' + v));
subscription2.unsubscribe();
    
subscription3 = source.subscribe(v => console.log('observerC: ' + v));
subscription3.unsubscribe();
    
subscription4 = source.subscribe(v => console.log('observerD: ' + v));
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.1.0/Rx.js"></script>

运行此代码段时,我们可以清楚地看到它没有为 Observer D 发出重复值,实际上它为每个订阅创建了新的排放量。怎么样?

在下次订阅发生之前,每个订阅都会被取消订阅。这有效地使refCount减少到零,没有进行多播。

问题在于realSource流未完成。因为我们不是多播,所以下一个订户通过ReplaySubject获得realSource的新实例,并且新的排放量与先前已经排放的排放量一起被预先添加。

因此,要修复您的流,多次调用昂贵的HTTP请求,您必须完成流,以便publishReplay知道它不需要重新订阅。

答案 1 :(得分:6)

通常:refCount表示只要有至少1个订阅者,流就是热/共享的 - 但是,如果没有订阅者,它将被重置/冷却。

这意味着如果您想要绝对确保不会执行任何操作,则不应使用refCount(),而只需使用connect流将其设置为热点。

作为补充说明:如果您在observer.complete()之后添加observer.next(5);,您也会得到预期的结果。

旁注:你真的需要在这里创建自己的自定义Obervable吗?在95%的情况下,现有的运算符足以满足给定的用例。

答案 2 :(得分:4)

这是因为您正在使用publishReplay()。它在内部创建了一个ReplaySubject实例,用于存储所有经过的值。

由于您使用Observable.create发出单个值,因此每次调用source.subscribe(...)时,都会在ReplaySubject中向缓冲区附加一个值。

您没有在每行的开头打印call,因为ReplaySubject在订阅时首先发出缓冲区,然后订阅自己的来源:

有关实施细节,请参阅:

使用publishReplay(1)时同样适用。首先,它从ReplaySubject发出缓冲的项目,然后从observer.next(5);

发出另一个项目