撰写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中,work
和dataBasedOncontrols
都是通过异步管道订阅的。控制台输出为
Data Fetched
Building Controls
Building Controls <- This shouldn't happen
Computed Data
ShareReplay
上的work
是否可以防止两个抽头函数执行两次?如果删除订阅dataBasedOncontrols
的元素,则会得到预期的行为。此外,当我在可观察的工作中添加第二个管道异步元素时,我得到了重复的行为,这使我相信ShareReplay(1)
不能正常工作。
有人可以解释为什么buildControls(data)
中的work
被执行两次。
答案 0 :(得分:1)
您使用的是rxjs
的确切版本?该行为在版本6.4.0中已更改。我将假设您使用的内容少于此,因为您的代码应该可以正常工作。
有两点可能导致重新订阅。
1)如果可观察的源抛出错误,则该错误将传播到所有当前订户。将来对共享可观察对象的任何订阅都会触发对源的重新订阅。
2)如果在调用refCount: true
(即shareReplay
)时在配置中传递了shareReplay({bufferSize: 1, refCount: true})
,并且源可观察者尚未完成,并且当前没有对共享可观察者的活动订阅。以后的任何订阅都将触发对源的重新订阅。
例如,如果您在一个订阅者上使用first
或takeUntil
,这很容易发生。第一个订阅者先于第二个订阅者完成。
除了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
,因此您必须切换到第一个签名才能使用它。