RxJS链接的ShareReplay无法正确多播

时间:2019-07-17 18:23:52

标签: angular rxjs6

撰写Observables时,ShareReplay无法按预期方式工作。以下是一个简单的示例。

apiData = this.api.get(url).pipe(
  tap(() => console.log('Data Fetched')),
  shareReplay(1)
);

work = this.apiData.pipe(
  tap(() => console.log('Building Controls')),
  tap((data) => this.buildControls(data)),
  shareReplay(1)
);

dataBasedOncontrols = this.work.pipe(
  tap(() => console.log('Computed Data')),
  switchMap(() => this.controlDataStream())
);

在我的.html中,workdataBasedOncontrols都是通过异步管道订阅的。控制台输出为

Data Fetched
Building Controls
Building Controls <- This shouldn't happen
Computed Data

ShareReplay上的work是否可以防止两个抽头函数执行两次?如果删除订阅dataBasedOncontrols的元素,则会得到预期的行为。此外,当我在可观察的工作中添加第二个管道异步元素时,我得到了重复的行为,这使我相信ShareReplay(1)不能正常工作。

有人可以解释为什么buildControls(data)中的work被执行两次。

1 个答案:

答案 0 :(得分:1)

您使用的是rxjs的确切版本?该行为在版本6.4.0中已更改。我将假设您使用的内容少于此,因为您的代码应该可以正常工作。

在版本> = 6.4.0(和5.4.0)中

有两点可能导致重新订阅。

1)如果可观察的源抛出错误,则该错误将传播到所有当前订户。将来对共享可观察对象的任何订阅都会触发对源的重新订阅。

2)如果在调用refCount: true(即shareReplay)时在配置中传递了shareReplay({bufferSize: 1, refCount: true}),并且源可观察者尚未完成,并且当前没有对共享可观察者的活动订阅。以后的任何订阅都将触发对源的重新订阅。

例如,如果您在一个订阅者上使用firsttakeUntil,这很容易发生。第一个订阅者先于第二个订阅者完成。

在版本<6.4.0中(版本5.4.0除外):

除了refCount不存在之外,规则几乎相同,因此它是隐含的。

我怀疑这可能是您的情况。您的第一个订阅者正在完成,而第二个订阅者正在订阅。尽管它必须在请求完成之前完成。如果请求完成,则整个链将完成,它将锁定到位,并且永远不要重新订阅。

如果可以,则升级到> = 6.4.0。添加了refCount属性,它的默认值为false,因此您甚至不必更改代码(尽管很明确,因为这是一个令人困惑的主题,很高兴)。

这在您的示例中应该是安全的,因为(我怀疑)最里面可观察到的是一个http.get调用,该调用应在完成时完成。

如果最里面的可观察物没有完成,那么这很危险,因为外面的可观察物也不会完成。根据情况,这可能不是一件坏事。

更新:请注意,要使用refCount选项,您必须使用新的签名。现在签名...

export function shareReplay<T>(config: ShareReplayConfig): MonoTypeOperatorFunction<T>;
export function shareReplay<T>(bufferSize?: number, windowTime?: number, scheduler?: SchedulerLike): MonoTypeOperatorFunction<T>;

第二个签名是旧的,直接在调用中使用了参数。第一个签名是新签名,它采用单个参数(配置对象)。配置对象具有refCount,因此您必须切换到第一个签名才能使用它。