当使用 shareReplay 与
refCount: true
时,我希望它在没有活动订阅时从源取消订阅,但它似乎挂在其他类型的引用上?我们在执行 http 请求时遇到了这种情况,该请求在发出后完成,但对于我使用的示例 of()
,它似乎做了同样的事情。
两个可观察量之间的唯一区别是其中一个以 take(1) 结尾。但在这两种情况下,他们的订阅者都complete和unsubscribe,并且所有take操作符应该在N次发射之后完成,所以它看起来多余,但它确实改变了行为。
但是你可以看到,如果没有
take(1)
,badObservable 仍然相信它有一个引用,所以它不会从源取消订阅,也不会重新运行请求。
如果您将
take(1)
从共享可观察量中移开并将其与调用者配对,感觉会更奇怪,因为这样两个多播可观察量在定义时实际上是相同的,但您必须确保使用 访问“好的”可观察量this.goodObservable.pipe(take(1)).subscribe({
以便共享可观察量正确地取消订阅/重新订阅原始源请求。但是,如果任何人出现并且突然执行 this.goodObservable.subscribe
操作,那么原始的共享可观察对象就会被所有未来的请求卡住,并且它永远不会从源取消订阅(在我们的例子中,永远不会再次发出 http 请求)。但重要的是,在这两种情况下 this.goodObservable.pipe(take(1)).subscribe
和 this.goodObservable.subscribe
订阅均已完成并声称已关闭/取消订阅!因此,从外部看,我看不到任何明显的差异,任何想知道“我是否需要 take(1) 或手动取消订阅”的人都会看到它已经完成/取消订阅,没有任何额外的内容,并且没有意识到仍然需要添加 take (1) 不要破坏底层的可观察值。
更新:
在 github 上深入研究后,我发现了一些与此相关的问题: https://github.com/ReactiveX/rxjs/issues/5587
https://github.com/ReactiveX/rxjs/issues/6760
https://github.com/ReactiveX/rxjs/issues/5894
看起来行为在某个时刻发生了变化,因此它不会重置导致的已完成的可观察值(并且显然仍然导致一些混乱)。在 refCount true 下获得预期的 shareReplay 功能的正确方法是仅使用 share:
share({
connector: () => new ReplaySubject(1),
resetOnError: true,
resetOnComplete: true,
resetOnRefCountZero: true
})
哪个有效。
我仍然不太明白的是,为什么 take(1) 会以某种方式绕过这个问题,即使对于在一次发射后完成的可观察量(如 http 请求)也是如此。在意识到源可观察对象在技术上也已完成之前,是否会以某种方式取消订阅并关闭它?
您正在共享一个已完成的可观察量,因为
of()
将同步发出一次,并立即完成。 HTTP 请求也会做同样的事情,只是异步的。
当您共享一个已完成的可观察值时,其结果将永远保存在底层 Replay 中,如文档中所述:
成功完成的源将永远缓存在 shareReplayed 可观察中,但错误的源可以重试。如果您修改示例以使用未完成的可观察量,您将看到结果实际上已共享并且行为符合预期:
https://stackblitz.com/edit/stackblitz-starters-s5ecqt?file=src%2Fmain.ts