我遇到了订阅2倍的场景。但是,如果我通过复制粘贴轮询器方法到我的组件中来摆脱这个服务,那么这个问题就解决了。
我运行的是Ionic 3,但我不认为这很重要。"rxjs":"5.4.3“
在ngDestroy()
上调用unsubscribe()
my-service.service.ts
public pollingDataReceived = new Subject<any>();
pollForData = (id: string) : Subscription => {
let stopPollingForStatus = false;
return Observable.interval(environment['pollingInterval'])
.takeWhile(() => !stopPollingForStatus)
.concatMap(() => {
return this.getAnalysisById(id);
})
.subscribe((analysisData) => {
if (analysisData) {
if (analysisData.status === 'questions') {
stopPollingForStatus = false;
}
else {
stopPollingForStatus = true;
const result = {
someData: 'whatever'
};
console.log('raise pollingDataReceived event');// This happens ONCE
this.pollingDataReceived.next(result);
}
}
else {
alert('no analysis data!');
}
});
}
my.component.ts
(订阅者)
private pollingData: Subscription;
someEventHandler() {
this.pollingData = this.pollForData(this.id);
}
ngOnInit(): void {
this.myService.pollingDataReceived.subscribe((data) => {
this.analysis = data.analysisData;
//This is getting called 2x. Maybe I double subscribed?
myNavigator.navigateToPageByStatus(myPage);
}, (err) => { console.log(err); });
}
ngOnDestroy() {
console.log('ngOnDestroy: unsubscribe pollingData in flow');// This does get called
this.pollingData.unsubscribe();
}
发布于 2019-04-09 21:37:45
我遇到了一个类似的问题,尝试下面的代码,这只是一个潜在问题的变通方法。
this.pollingDataReceived.next(result);
this.pollingDataReceived = new BehaviorSubject<any>(); // Here, create new object with null value
发布于 2019-04-09 21:35:53
除非在组件级别提供,否则Angular Services的行为就像单例一样。因此,pollingDataReceived
主体将继续持有其订阅,直到服务进入作用域。
在你的例子中,之前访问的对this.myService.pollingDataReceived
的过时订阅也被解雇了。因此,订阅者在重新访问时在上触发两次。
在类似于pollingData
的ngDestroy
上清理它可以解决这个问题。
发布于 2019-04-09 23:20:37
处理程序触发两次的原因是,当您多次推送到此主题时,您永远不会取消订阅服务pollingDataReceived
主题。您的服务可能只创建了一次,而您的组件通过页面重新访问被重新创建了多次。组件被销毁,但它们对同一主题的订阅仍然完好无损。
解决此问题的最简单方法是以@ashish.gd suggested的身份在component ngOnDestroy
method中取消订阅此主题。
每次使用()时,都会创建新主题,这可能会导致内存泄漏。因为您的旧订阅将继续收听旧主题。当心。
虽然最简单的方法是取消订阅pollingDataReceived
主题,但更好的方法是让您的服务pollForData()
返回一个可观察对象。
除非你需要多个用户来阅读same pollingDataReceived
主题--你不需要在那里订阅。下面是一个反映这一想法的示例代码:
pollForData = (id: string) : Observable => {
return Observable.interval(environment['pollingInterval'])
.concatMap(() => this.getAnalysisById(id))
.takeWhile(data => data.status === 'questions')
// other handling logic goes here
// .map(), .switchMap(), etc
// NOTE: no subscription here
}
在控制器中,你可以像这样做
// just an utility subject to track component destruction
onDestroy$ = new Subject();
someEventHandler() {
this.pollForData(this.id).pipe(
// this will ensure that subscription will be completed on component destruction
takeUntil(this.onDestroy$)
).subscribe((data) => {
this.analysis = data.analysisData;
myNavigator.navigateToPageByStatus(myPage);
}, (err) => { console.log(err); });
}
ngOnDestroy() {
this.onDestroy$.next(void 0);
}
--
备注1:似乎目前你的服务是在模块级提供的,这意味着如果你有两个消费者同时请求pollForData(id)
(例如,一个页面上的两个组件)-这两个请求都会“写入”到中相同的主题。容易出错的行为。
注释2:目前在您的代码中,只有在流上获得一个新的事件之后,才会运行takeWhile
谓词
Observable.interval(environment['pollingInterval'])
.takeWhile(() => !stopPollingForStatus)
这意味着您将在内存中为environment['pollingInterval']
时间进行不必要的订阅。这不应该是关键的,但根据时间的不同,它可能会带来麻烦。
--
希望这对你有所帮助,祝你编码愉快!
https://stackoverflow.com/questions/55593785
复制相似问题