在下面的代码中,我创建了一个简单的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);
这导致以下大理石图
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中,我很无能为力
如果可能,我想使用现有的操作员或主题来解决这个问题。我的直觉告诉我,我必须用这样的逻辑来创造一个不同的主题,但我还没到那里。
答案 0 :(得分:1)
shareReplay
在返回的observable的剩余生命周期内保留相同的基础ReplaySubject
实例。
ReplaySubject
完成后,您无法再添加任何值,但仍会重播。所以...
i
从0
增加到1
。onNext(i)
,然后发送onCompleted()
。onCompleted()
信号完成ReplaySubject
内的shareReplay
,这意味着从现在开始,该共享的observable将只重放其拥有的值(即1)并完成。另一个独立的问题是,由于你共享了observable,它只会一次调用订阅者函数 。这意味着i
只会增加一次。因此,即使您没有onCompleted
并杀死基础ReplaySubject
,您最终也不会将其增加到2
。
快速说明的方法是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返回取消语义。