如何通过重新连接实现shareReplay?

时间:2016-04-20 19:29:27

标签: javascript reactive-programming rxjs rxjs5

在下面的代码中,我创建了一个简单的observable,它生成一个值然后完成。然后我分享了那个可观察的重播最后一个项目,然后重复了3次。第一个是右边的,第二个是在产生值之前,第三个是在产生值之后,可观察到的已经完成。

let i = 0;
let obs$ = Rx.Observable.create(obs => {
  console.log('Creating observable');
  i++;
  setTimeout(() => {
     obs.onNext(i);
     obs.onCompleted();
  }, 2000);
}).shareReplay(1);

obs$.subscribe(
  data => console.log(`s1: data = ${data}`),
  () => {},
  () => console.log('finish s1')
);

setTimeout( () => {
  obs$.subscribe(
    data => console.log(`s2: data = ${data}`),
    () => {},
    () => console.log('finish s2')

  );  
}, 1000);

setTimeout( () => {
  obs$.subscribe(
    data => console.log(`s3: data = ${data}`),
    () => {},
    () => console.log('finish s3')

  );  
}, 6000);

你可以execute this on jsbin

这导致以下大理石图

Actual
s1: -----1$
s2:   \--1$
s3:           \1$

但我希望

Expected
s1: -----1$
s2:   \--1$
s3:           \----2$

我可以理解为什么有人想要第一个行为,但我的理由是,与这个例子不同,在我返回一个数字的地方,我可能会返回一个容易受到取消订阅行为的对象,例如数据库连接。如果上面的大理石图表示数据库连接,那么在dispose方法中我调用db.close(),在第三个订阅上我将有一个异常,因为我作为值接收已发布的数据库处理程序。 (因为当第二个订阅完成refCount = 0并且源被处理时)。

这个例子另外一个奇怪的事情是,即使它正在解决 第一个值并在之后完成,它订阅源两次(正如你可以看到重复的“Creating observable”)

我知道this github issue谈到了这一点,但我缺少的是:

如果源observable尚未完成,如何完成(refCount = 0),则如何实现(在RxJs4和5中)可以重放最后一项的共享observable,重新创建observable。

在RxJs5中,我认为共享方法解决了问题的重新连接部分,而不是共享部分。

在RxJs4中,我很无能为力

如果可能,我想使用现有的操作员或主题来解决这个问题。我的直觉告诉我,我必须用这样的逻辑来创造一个不同的主题,但我还没到那里。

1 个答案:

答案 0 :(得分:1)

关于shareReplay的一点:

shareReplay在返回的observable的剩余生命周期内保留相同的基础ReplaySubject实例。

ReplaySubject完成后,您无法再添加任何值,但仍会重播。所以...

  1. 您第一次订阅了observable并且超时开始。这会将i0增加到1
  2. 您第二次订阅了observable,超时已经开始了。
  3. 超时回叫会触发并发送onNext(i),然后发送onCompleted()
  4. onCompleted()信号完成ReplaySubject内的shareReplay,这意味着从现在开始,该共享的observable将只重放其拥有的值(即1)并完成。
  5. 关于共享可观察量的一点意见:

    另一个独立的问题是,由于你共享了observable,它只会一次调用订阅者函数 。这意味着i只会增加一次。因此,即使您没有onCompleted并杀死基础ReplaySubject,您最终也不会将其增加到2

    这不是RxJS 5

    快速说明的方法是onNext vs next。您目前在示例中使用RxJS 4,但是您已使用RxJS 5对其进行了标记,并且您在RxJS 5中发现了一个问题.RxJS 5是测试版,而新版本则完全重写了RxJS 4。 API更改主要是为了匹配es-observable proposal which is currently at stage 1

    更新了示例

    I've updated your example to give you your expected results

    基本上,你想为前两个调用使用observable的共享版本,为第三个调用使用原始的observable。

    let i = 0;
    let obs$ = Rx.Observable.create(obs => {
      console.log('Creating observable');
      i++;
      setTimeout(() => {
         obs.onNext(i);
         obs.onCompleted();
      }, 2000);
    })
    
    
    let shared$ = obs$.shareReplay(1);
    
    shared$.subscribe(
      data => console.log(`s1: data = ${data}`),
      () => {},
      () => console.log('finish s1')
    );
    
    setTimeout( () => {
      shared$.subscribe(
        data => console.log(`s2: data = ${data}`),
        () => {},
        () => console.log('finish s2')
    
      );  
    }, 1000);
    
    setTimeout( () => {
      obs$.subscribe(
        data => console.log(`s3: data = ${data}`),
        () => {},
        () => console.log('finish s3')
    
      );  
    }, 6000);
    

    相依

    另外,protip:请务必为调用clearTimeout的自定义observable返回取消语义。