在查看代码示例时,我经常看到没有取消订阅服务内部的可观察对象的情况。
这里是一个例子:
export class AuthGuard implements CanActivate {
private isLoggedIn: boolean;
private isLoggedIn$: Observable<boolean>;
constructor(private authService: AuthService, private router: Router) {
this.isLoggedIn$ = this.authService.isLoggedIn();
this.isLoggedIn$.subscribe(res => {
if (res) {
this.isLoggedIn = true;
}
else {
this.isLoggedIn = false;
}
});
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
if (this.isLoggedIn) {
return true;
}
else {
this.router.navigate(['login']);
return false;
}
}
}
在这种情况下,您是否有理由不退订可观察到的this.isLoggedIn $?还是上面的例子只是编码错误导致内存泄漏?
答案 0 :(得分:3)
对于核心服务,取消订阅实际上不是必需的,因为服务的寿命与应用程序的寿命相同,并且该服务是单例的。问题是您必须确保始终将其作为单例使用(有关更好的解决方案,请参阅下文)
对于组件服务,应将取消订阅置于服务的ngOnDestroy
内,因为服务实际上可以实现此NgOnDestroy
钩子。更好的方法是使用takeUntil(this.destroy)
管道,并在销毁时将其发出。
一种更好的方法是在模板中使用async
管道,而不是真正地直接订阅这些东西。
另一方面,在您的后卫中,您可以使用take(1)
管道,这将进行第一个发射并立即取消订阅,而无需您这样做,这将使您的示例变成这样。如您所见,没有在代码内订阅:
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
return this.authService.isLoggedIn().pipe(
take(1),
tap((loggedIn) => {
if (!loggedIn) {
this.router.navigate(['login'])
}
})
);
}
}
最重要的是,请尝试使用异步模板内部和rxjs管道函数使应用程序保持Observable流,而不是订阅和存储子结果。要使isLoggedIn()
在订阅时发出最后一个结果,可以使用shareReplay()
管道,也可以使用BehaviourSubject
作为开头。
答案 1 :(得分:0)
在查看代码示例时,我经常看到没有取消订阅服务内部的可观察对象的情况。
您正在寻找错误。
在这种情况下,您是否有理由不退订可观察到的this.isLoggedIn $?
如果您想泄漏内存。
还是上面的示例只是错误的编码导致内存泄漏?
是的,它正在泄漏内存。在this
引用的对象被销毁之后,订阅函数将继续执行。如果此对象永不销毁,则可能永远不会发生,但这只是代码示例,它将使在临时创建对象的单元测试中失败。
标记为@Injectable()
的对象通常表现为单例或具有弱引用。它可能会工作一段时间,但是在临时情况下使用它会泄漏内存。