简体   繁体   English

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

[英]How can I achieve a shareReplay with reconnection?

In the following code, I create a simple observable that produces one value and then complete.在下面的代码中,我创建了一个简单的 observable,它产生一个值然后完成。 Then I share that observable replaying the last item and suscribe 3 times.然后我分享那个 observable 重放最后一个项目并订阅 3 次。 The first right after, the second one before the value is produced and the third time after value is produced and the observable has completed.第一次紧随其后,第二次在产生价值之前,第三次在价值产生并且 observable 完成之后。

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);

You can execute this on jsbin你可以在jsbin上执行这个

This results in the following marble diagram这导致以下大理石图

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

But I would expect但我希望

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

I can understand why someone would like to have the first behaviour, but my reasoning is that, unlike this example, where I'm returning a number, I could be returning an object susceptible to unsubscribe behaviour, for example a database connection.我可以理解为什么有人想要第一个行为,但我的推理是,与这个例子不同,我返回一个数字,我可能返回一个易受取消订阅行为影响的对象,例如数据库连接。 If the above marble diagram represents a database connection, where in the dispose method I call a db.close() , on the third subscription I would have an exception, because I'm receiving as value a database handler that was released.如果上面的弹珠图表示一个数据库连接,在 dispose 方法中我调用了db.close() ,那么在第三个订阅中我会有一个例外,因为我收到的值是一个已发布的数据库处理程序。 (because when the second subscription finished refCount = 0 and the source is disposed). (因为当第二个订阅完成时 refCount = 0 并且源被释放)。

Also another weird thing this example has, is that even it's resolving with the first value and completing just after, its subscribing to the source twice (as you can see by the duplicated "Creating observable")这个例子还有另一个奇怪的事情是,即使它用第一个值解析并在之后完成,它订阅源两次(正如你在重复的“Creating observable”中看到的那样)

I know this github issue talks about this but what I'm missing is:我知道这个 github 问题谈到了这个,但我缺少的是:

How can achieve (both in RxJs4 and 5) a shared observable that can replay the last item if the source observable hasn't completed, and if its done (refCount = 0), recreate the observable.如何实现(在 RxJs4 和 5 中)共享的 observable,如果源 observable 尚未完成,则可以重放最后一项,如果完成(refCount = 0),则重新创建 observable。

In RxJs5 I think the share method solves the reconnecting part of my problem, but not the sharing part.在 RxJs5 中,我认为 share 方法解决了我问题的重新连接部分,但不能解决共享部分。

In RxJs4 I'm clueless在 RxJs4 我一无所知

If possible I would like to solve this using existing operators or subjects.如果可能的话,我想使用现有的运算符或主题来解决这个问题。 My intuition tells me I would have to create a different Subject with such logic, but I'm not quite there yet.我的直觉告诉我我必须用这样的逻辑创建一个不同的主题,但我还没有完全到位。

A bit on shareReplay:关于 shareReplay 的一点:

shareReplay keeps the same underlying ReplaySubject instance for the rest of the lifetime of the returned observable. shareReplay在返回的 observable 的剩余生命周期中保持相同的底层ReplaySubject实例。

Once ReplaySubject completes, you can't put any more values into it, but it will still replay.一旦ReplaySubject完成,您就不能ReplaySubject任何值,但它仍会重播。 So...所以...

  1. You subscribe to the observable the first time and the timeout starts.您第一次订阅 observable 并且超时开始。 This increments i from 0 to 1 .这将i0增加到1
  2. You subscribe to the observable the second time and the timeout is already going.您第二次订阅了 observable 并且超时已经过去了。
  3. The timeout callback fires and sends out onNext(i) , then onCompleted() .超时回调触发并发送onNext(i) ,然后是onCompleted()
  4. onCompleted() signal completes the ReplaySubject inside the shareReplay , meaning that from now on, that shared observable will simply replay the value it has (which is 1) and complete. onCompleted()信号完成了ReplaySubjectshareReplay ,这意味着从现在开始,共享的 observable 将简单地重放它所拥有的值(即 1)并完成。

A bit on shared observables in general:关于共享可观察的一般情况:

Another, separate issue is that since you shared the observable, it's only ever going to call the subscriber function one time .另一个单独的问题是,由于您共享了 observable,它只会调用一次订阅者函数。 That means that i will only ever be incremented one time.这意味着i只会增加一次。 So even if you didn't onCompleted and kill your underlying ReplaySubject , you're going to end up not incrementing it to 2 .因此,即使您没有onCompleted并杀死您的底层ReplaySubject ,您最终也不会将其增加到2

This isn't RxJS 5这不是 RxJS 5

A quick way to tell is onNext vs next .一个快速判断的方法是onNextnext You're currently using RxJS 4 in your example, but you've tagged this with RxJS 5, and you've sighted an issue in RxJS 5. RxJS 5 is beta and a new version that is a complete rewrite of RxJS 4. The API changes were done mostly to match the es-observable proposal which is currently at stage 1您当前在示例中使用的是 RxJS 4,但是您已经用 RxJS 5 标记了它,并且您在 RxJS 5 中发现了一个问题。RxJS 5 是测试版,是一个完全重写 RxJS 4 的新版本。 API 更改主要是为了匹配目前处于第 1 阶段es-observable 提案

Updated example更新示例

I've updated your example to give you your expected results我已经更新了您的示例,以便为您提供预期的结果

Basically, you want to use a shared version of the observable for the first two calls, and the original observable for the third one.基本上,您希望在前两次调用中使用共享版本的 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);

Unrelated无关

Also, protip: be sure to return a cancellation semantic for your custom observable that calls clearTimeout .另外,提示:确保为调用clearTimeout自定义 observable 返回取消语义。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM