取消订阅服务内部的 Observables?

Posted

技术标签:

【中文标题】取消订阅服务内部的 Observables?【英文标题】:Unsubscribing from Observables inside services? 【发布时间】:2019-01-18 08:05:17 【问题描述】:

在查看代码示例时,我经常看到服务内部的 observable 没有被取消订阅的情况。

这是一个例子:

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$?还是上面的例子只是糟糕的编码导致内存泄漏?

【问题讨论】:

Is it necessary to unsubscribe from observables created by Http methods?的可能重复 brianflove.com/2016/12/11/anguar-2-unsubscribe-observables 请访问这篇文章无论如何上面它只是糟糕的编码示例 我在问这个问题之前已经阅读了这篇文章....这篇文章没有涉及任何关于是否应该取消订阅 observables 的问题,即使 observable 不包含在组件中而是包含在服务 @Vlad274:我现在不必取消订阅由 http 方法创建的 observables ......但是由 Services 创建的 observables 呢? 【参考方案1】:

对于核心服务,取消订阅并不是真正必要的,因为服务与应用程序一样存在,而且服务是单例的。问题是您必须确保始终将其用作单例(要获得更好的解决方案,请阅读下文)

对于组件服务,取消订阅应该放在服务的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 开头。

【讨论】:

我个人认为不要取消订阅,因为“它永远存在”是一种不好的做法。事情可能会发生变化,这是一个实现细节。如果您确定生命周期,您真的会记得经历并修复所有问题吗? @IngoBürk 这正是我在第一段中所说的 :) 并在其余答案中对此进行了更详细的说明【参考方案2】:

在查看代码示例时,我经常看到服务内部的 observable 没有被取消订阅的情况。

您正在查看一个错误。

在这种情况下,您是否有理由不取消订阅可观察的 this.isLoggedIn$?

如果你想泄漏内存。

或者上面的例子只是糟糕的编码导致内存泄漏?

是的,它正在泄漏内存。订阅函数会在this 引用的对象被销毁后继续执行。如果这个对象永远不会被销毁,它可能永远不会发生,但这是一个代码示例,它会导致临时创建对象的单元测试失败。

带有@Injectable() 标记的对象通常表现得像单例或具有弱引用。它可能会工作一段时间,但一旦你在临时情况下使用它,它就会泄漏内存。

【讨论】:

以上是关于取消订阅服务内部的 Observables?的主要内容,如果未能解决你的问题,请参考以下文章

取消订阅 RxJS Observables

取消订阅 Rxjs Observables

我应该取消订阅根 Angular 组件中的 observables 吗?

typescript 取消订阅Observables

角 2 |订阅 Observables

Angular RxJS Observable:takeUntil 与使用订阅取消订阅 [关闭]