【问题标题】:Angular observables - Do I need unsubscribe if no subscription?Angular observables - 如果没有订阅,我需要取消订阅吗?
【发布时间】:2019-11-08 21:18:55
【问题描述】:

我正在使用最新的 Angular 8,并且对 observables 的概念很陌生。我有一个问题,如果我直接调用一个可观察对象而不将其应用于订阅变量,我还需要取消订阅吗?以下是我想知道是否需要退订的场景? 非常感谢提前

场景 1 - 从组件调用 httpService:

Service - httpService

     getContactsHttp(){
         let headers: any = new HttpHeaders(this.authService.getHeadersClient());
         return this.httpClient.get('/contacts', {headers: headers})
          .pipe(timeout(this.authService.getTimeoutLimit('normal')));
        }

Component - Calling getContactsHttp and sorting response

getContacts() {
 this.httpService.getContactsHttp().subscribe((data:any[])=>{
  this.records = this.sortData(data)
 })
}

场景 2 - 在组件中订阅的 observable 上

contacts$: new Subject<any[]>;

ngOnInit() {
  this.getContacts();
  this.contacts$.subscribe((data:any[])=>{
    this.records = this.sortData(data);
  })
}

getContacts() {
    this.httpService.getContactsHttp().subscribe((data:ContactSearch[])=>{      
      this.contacts$.next(data);
    })
  }

服务 - httpService

     getContactsHttp(){
         let headers: any = new HttpHeaders(this.authService.getHeadersClient());
         return this.httpClient.get('/contacts', {headers: headers})
          .pipe(timeout(this.authService.getTimeoutLimit('normal')));
        }

【问题讨论】:

  • 回答下面的评论,不退订是内存泄漏。

标签: angular rxjs observable


【解决方案1】:

简短的回答,是的,您仍然将组件中的 observable 退订到 avoid subscription leaks。我首选的方法之一是使用 takeUntil() 运算符。

这是您可以在组件中使用它的方式。

private unsubscribe: Subject<void> = new Subject();

ngOnDestroy() {
  this.unsubscribe.next();
  this.unsubscribe.complete();
}

getContacts() {
  this.httpService.getContactsHttp()
    .pipe(
      takeUntil(this.unsubscribe),
    ).subscribe((data:ContactSearch[])=>{      
      this.contacts$.next(data);
    });
 }

正如Brian Love所解释的,

  • 首先,我们导入 takeUntil() 运算符以及 Subject 班级。
  • 接下来,我们定义一个名为 unsubscribe 的私有实例属性, 这是一个主题。
  • 我们还创建了一个新的 Subject 实例,定义 泛型类型为 void。我们使用 takeUntil() 操作符 调用 subscribe() 之前的 pipe() 方法,提供取消订阅 可观察的。
  • 在 ngOnDestroy() 生命周期方法中,我们发出一个 next() 通知,然后完成()取消订阅观察。这 订阅现已完成,我们已立即取消订阅 当在我们的生命周期中调用 ngOnDestroy() 方法时 组件。

【讨论】:

  • ..并且通常将 takeUntil 设置为管道中的最后一个运算符
【解决方案2】:

1) 直接调用http调用一般不需要取消订阅。即使组件被销毁,销毁后订阅完成的开销也是微不足道的。 如果快速切换组件,您需要在此处取消订阅。取消订阅也会取消 http 请求,因此如果需要,请取消订阅。

退订不会造成任何伤害。如果您不确定,请始终退订。

2) 当您订阅一个在您的组件被销毁时未完成的可观察对象时,您确实需要取消订阅。否则这将导致内存(和性能)泄漏。因为 observable 本身持有对订阅的引用,而订阅持有对组件的引用,所以组件永远不会从内存中清除,并且订阅中描述的操作将一直运行直到 observable 完成,在您的情况下永远不会.组件的每个实例都会发生这种情况。

解决方案

我将分享两个关于简化退订负担的流行选项。扩展 @amanagg1204 答案,您可以创建一个基础组件,您可以从中扩展所有未来的组件。您可以在其中包含自定义运算符。这样做有一个缺点 - 如果您需要在组件中使用 ngOnDestroy,则始终必须调用 super.ngOnDestroy()

import { OnDestroy } from "@angular/core";
import { Subject, MonotypeOperatorFunction } from "rxjs";
import { takeUntil } from "rxjs/operators";

export abstract class UnsubscribeComponent implements OnDestroy {
  protected destroyed$: Subject<void> = new Subject();

  ngOnDestroy(): void {
    this.destroyed$.next();
    this.destroyed$.complete();
  }

  takeUntilDestroyed<T>(): MonoTypeOperatorFunction<T> {
      return takeUntil(this.destroyed$);
  }
}

export class Component extends UnsubscribeComponent {
   ngOnInit() {
      this.contacts$.pipe(
              this.takeUntilDestroyed(),
          ).subscribe((data:any[])=>{
          this.records = this.sortData(data);
      });
   }

  // WARNING - if you declare your ngOnDestroy in the component
  ngOnDestroy() {
     // DO NOT FORGET to call this
     super.ngOnDestroy();
     doYourStuff();
  }

}

其他选项(我更喜欢)是没有父抽象类(尽管它也可以这样实现),而是使用名为 subsink (npm i subsink --save) 的实用程序

import { SubSink } from 'subsink';

export class SampleComponent implements OnInit, OnDestroy {
  private subs = new SubSink();

  ngOnInit(): void {
    // Just put it into sink.
    this.subs.sink = this.contacts$.subscribe((data:any[])=>{
      this.records = this.sortData(data);
    });
    // call repeatedly
    this.subs.sink = this.otherService$.subscribe((data:any[])=>{
      this.things = this.sortData(data);
    });
  }

  ngOnDestroy(): void {
    // this will unsubscribe all
    this.subs.unsubscribe();
  }
}

【讨论】:

  • 感谢您的解决方案。一种快速是必要的 SubSink 实用程序。我们不能只使用 Subscription 属性类型吗?即私人订阅:订阅。然后销毁 this.subs.unsubscribe()?
  • 您可以拥有subs: Subscription[] = [],调用this.subs.push(...subscribe(...)) 并在销毁时调用subs.forEach(s =&gt; s.unsubscribe())。 subsink 只是稍微简化了一点。
  • @kvetis:你的代码没有错字吗? MonoTypeOperatorFunction。而且在SampleComponent 中,我们必须在构造函数中有一个超级调用。
  • @k.vincent 感谢您的关注。修正了错字。不确定SampleComponent 中的super 调用是什么意思,因为它没有构造函数,也没有继承自任何东西。
  • @kvetis:不客气。 super() - 我看看它是否没有构造函数,不需要。但我认为在几乎所有情况下,我们都会有一个构造函数,如果它没有在构造函数中调用,我们会得到一个错误:super()
【解决方案3】:

是的,总是退订。您有多种退订方式,具体如下:

-使用takeUntil()

-take(1)

-unsubscribe()中的ngOnDestroy()

-使用async 管道

是的,httpClient 返回一个冷的 observable,但是这个问题将解释你为什么仍然应该unsubscribe。 (看第二个得票最高的答案)

Is it necessary to unsubscribe from observables created by Http methods?

https://blog.angularindepth.com/why-you-have-to-unsubscribe-from-observable-92502d5639d0

附注:

1) 切勿订阅该服务。我们处于 Angular 世界中,我们需要以一种被动的方式思考,在服务中购买订阅会阻止您使用该 observable,以防您想将其与其他东西结合使用。

2) 在使用 observables 时开始使用声明式方法。

3) 停止使用any,这不是一个好习惯。充分利用类和接口,这样您的代码将更具可读性。

声明式方法的好处: - 利用 RxJs 可观察对象和操作符的力量 - 有效地结合流 -Easy share observables - 随时响应用户操作

您可能想知道声明式方法是什么样的?

您将这样做,而不是返回 observables 的方法。

SERVICE.TS

  yourObservableName$ = this.httpClient.get('/contacts', {headers: headers})
              .pipe(timeout(this.authService.getTimeoutLimit('normal')));

COMPONENT.TS

this.httpService.yourObservableName$.subscribe(...)

【讨论】:

  • 谢谢,但是 yourObservableName$ 的类型定义是什么?如果我将其定义为 yourObservableName$=Observable 我会收到以下错误:Type 'Observable' is not assignable to type 'Observable'。
  • 一切顺利,get 必须作为 this.httpClient.get
  • Pato 在您建议的解决方案中,我如何将参数从我的 component.ts 传递给 Service.ts 中的 httpClient?
  • 如果你传递参数,那么我会像你一样在一个函数中做。
  • 也感谢 service.ts 让我让它在函数中工作我还必须添加 yourObservableName$.subscribe() 才能调用 http 请求
【解决方案4】:

正如许多人已经指出的那样,http 返回一个 Cold Observable,但您仍然应该取消订阅该 observable。我会建议一种更好的方式来管理取消订阅,这样您就不必手动添加 ngOnDestroy()生命周期钩子每次。 我将首先创建一个取消订阅组件,如下所示

import { OnDestroy } from "@angular/core";
import { Subject } from "rxjs";

export abstract class UnsubscribeComponent implements OnDestroy {
  protected destroyed$: Subject<void> = new Subject();

  ngOnDestroy(): void {
    this.destroyed$.next();
    this.destroyed$.complete();
  }
}

然后在每个组件类声明中扩展它(在需要的地方)

export class ABC extends UnsubscribeComponent

在构造函数中调用 super()

constructor(your dependencies) {
  super()
}

最后,根据您的订阅,执行以下操作

this.obs$.pipe(takeUntil(this.destroyed$)).subscribe(() => {
  // some logic here
})

另一件事是 takeUntil 应该是管道中的最后一个运算符。希望对您有所帮助。

【讨论】:

    【解决方案5】:

    是的,取消订阅 ngOnDestroy 生命周期方法中的所有订阅是一个很好的做法,您可以通过在类级别变量中获取每个订阅的引用然后手动取消订阅来做到这一点!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-02
      • 1970-01-01
      • 2019-01-31
      • 2017-03-26
      • 2020-10-28
      • 2017-05-12
      • 2020-07-23
      • 2019-02-11
      相关资源
      最近更新 更多