什么时候我应该存储订阅实例和调用unsubscribe()在ngOnDestroy生命周期,什么时候我可以简单地忽略它们?

保存所有订阅会给组件代码带来很多麻烦。

HTTP客户端指南忽略这样的订阅:

getHeroes() {
  this.heroService.getHeroes()
                  .subscribe(
                     heroes => this.heroes = heroes,
                     error =>  this.errorMessage = <any>error);
}

同时,《航路指南》指出:

最终,我们会航行到别的地方。路由器将从DOM中移除这个组件并销毁它。在那之前,我们得把自己弄干净。具体来说,我们必须在Angular销毁该组件之前取消订阅。如果不这样做,可能会产生内存泄漏。 我们在ngOnDestroy方法中取消订阅我们的可观察对象。

private sub: any;

ngOnInit() {
  this.sub = this.route.params.subscribe(params => {
     let id = +params['id']; // (+) converts string 'id' to a number
     this.service.getHero(id).then(hero => this.hero = hero);
   });
}

ngOnDestroy() {
  this.sub.unsubscribe();
}

当前回答

你可以使用最新的订阅类来取消对Observable的订阅,代码不会那么乱。

我们可以用普通变量来做这个,但它会在每次新的订阅上覆盖上一次订阅,所以要避免这种情况,当你处理更多数量的可观察对象时,这种方法是非常有用的,以及像behaviour Subject和Subject这样的可观察对象类型

订阅

表示一个一次性资源,比如Observable的执行。订阅有一个重要的方法,即unsubscribe,它不接受参数,只处理订阅所持有的资源。

你可以用两种方式来使用它,

you can directly push the subscription to Subscription Array subscriptions:Subscription[] = []; ngOnInit(): void { this.subscription.push(this.dataService.getMessageTracker().subscribe((param: any) => { //... })); this.subscription.push(this.dataService.getFileTracker().subscribe((param: any) => { //... })); } ngOnDestroy(){ // prevent memory leak when component destroyed this.subscriptions.forEach(s => s.unsubscribe()); } using add() of Subscription subscriptions = new Subscription(); this.subscriptions.add(subscribeOne); this.subscriptions.add(subscribeTwo); ngOnDestroy() { this.subscriptions.unsubscribe(); }

订阅可以保存子订阅并安全地取消所有订阅。这个方法处理可能的错误(例如,如果任何子订阅为空)。

希望这能有所帮助。:)

其他回答

你不需要有一堆订阅和取消手动订阅。使用Subject和takeUntil组合来像boss一样处理订阅:

import { Subject } from "rxjs"
import { takeUntil } from "rxjs/operators"

@Component({
  moduleId: __moduleName,
  selector: "my-view",
  templateUrl: "../views/view-route.view.html"
})
export class ViewRouteComponent implements OnInit, OnDestroy {
  componentDestroyed$: Subject<boolean> = new Subject()

  constructor(private titleService: TitleService) {}

  ngOnInit() {
    this.titleService.emitter1$
      .pipe(takeUntil(this.componentDestroyed$))
      .subscribe((data: any) => { /* ... do something 1 */ })

    this.titleService.emitter2$
      .pipe(takeUntil(this.componentDestroyed$))
      .subscribe((data: any) => { /* ... do something 2 */ })

    //...

    this.titleService.emitterN$
      .pipe(takeUntil(this.componentDestroyed$))
      .subscribe((data: any) => { /* ... do something N */ })
  }

  ngOnDestroy() {
    this.componentDestroyed$.next(true)
    this.componentDestroyed$.complete()
  }
}

@acumartini在评论中提出了另一种方法,使用takeWhile而不是takeUntil。你可能更喜欢它,但要注意,这样你的Observable的执行就不会在组件的ngDestroy上被取消(例如,当你进行耗时的计算或等待来自服务器的数据时)。方法没有这个缺陷,它会导致立即取消请求。感谢@AlexChe在评论中的详细解释。

代码如下:

@Component({
  moduleId: __moduleName,
  selector: "my-view",
  templateUrl: "../views/view-route.view.html"
})
export class ViewRouteComponent implements OnInit, OnDestroy {
  alive: boolean = true

  constructor(private titleService: TitleService) {}

  ngOnInit() {
    this.titleService.emitter1$
      .pipe(takeWhile(() => this.alive))
      .subscribe((data: any) => { /* ... do something 1 */ })

    this.titleService.emitter2$
      .pipe(takeWhile(() => this.alive))
      .subscribe((data: any) => { /* ... do something 2 */ })

    // ...

    this.titleService.emitterN$
      .pipe(takeWhile(() => this.alive))
      .subscribe((data: any) => { /* ... do something N */ })
  }

  ngOnDestroy() {
    this.alive = false
  }
}

视情况而定。如果通过调用someObservable.subscribe(),您开始占用一些资源,当组件的生命周期结束时必须手动释放这些资源,那么您应该调用subscription .unsubscribe()来防止内存泄漏。

让我们仔细看看你的例子:

getHero()返回http.get()的结果。如果你查看angular 2的源代码,会发现http.get()创建了两个事件监听器:

_xhr.addEventListener('load', onLoad);
_xhr.addEventListener('error', onError);

通过调用unsubscribe(),你可以取消请求和监听器:

_xhr.removeEventListener('load', onLoad);
_xhr.removeEventListener('error', onError);
_xhr.abort();

请注意,_xhr是特定于平台的,但我认为在您的情况下,可以安全地假设它是XMLHttpRequest()。

通常,这已经足够证明需要手动unsubscribe()调用了。但是根据WHATWG规范,XMLHttpRequest()一旦“完成”就会受到垃圾收集的影响,即使有事件监听器附加到它。所以我想这就是为什么angular 2官方指南省略了unsubscribe(),让GC清理侦听器。

至于第二个例子,它取决于参数的实现。从今天起,angular官方指南不再显示取消订阅参数。我再次查看了src,发现params只是一个行为主体。由于没有使用事件侦听器或计时器,也没有创建全局变量,因此省略unsubscribe()应该是安全的。

你的问题的底线是总是调用unsubscribe()来防止内存泄漏,除非你确定可观察对象的执行不会创建全局变量、添加事件侦听器、设置计时器或做任何其他导致内存泄漏的事情。

如果有疑问,请查看该可观察对象的实现。如果可观察对象已经在其unsubscribe()中写入了一些清理逻辑,这通常是构造函数返回的函数,那么你有充分的理由认真考虑调用unsubscribe()。

基于:使用类继承来钩子到Angular 2的组件生命周期

另一种通用方法:

导出抽象类UnsubscribeOnDestroy实现OnDestroy { protected d$: Subject<any>; 构造函数(){ 这一点。d$ = new Subject<void>(); const f = this.ngOnDestroy; 这一点。ngOnDestroy = () => { f (); this.d $ . next (); this.d .complete美元(); }; } public ngOnDestroy() { / /空操作 } }

并使用:

@ component ({ 选择器:“my-comp”, 模板:“ }) 导出类RsvpFormSaveComponent扩展UnsubscribeOnDestroy实现OnInit { 构造函数(){ 超级(); } ngOnInit(): void { Observable.of (bla) .takeUntil (this.d $) .subscribe(val => console.log(val)); } }

Angular 2官方文档提供了一个关于何时退订以及何时可以安全忽略的解释。看看这个链接:

https://angular.io/docs/ts/latest/cookbook/component-communication.html !# bidirectional-service

寻找标题为“父母和孩子通过服务通信”的段落,然后是蓝色方框:

注意,当AstronautComponent被销毁时,我们捕获了订阅并取消了订阅。这是一个内存泄漏保护步骤。在这个应用程序中没有实际的风险,因为AstronautComponent的生命周期与应用程序本身的生命周期相同。在更复杂的应用程序中,这并不总是正确的。 我们没有将这个守卫添加到MissionControlComponent中,因为作为父组件,它控制着MissionService的生命周期。

我希望这对你有所帮助。

更新Angular 9和Rxjs 6解决方案

在Angular组件的ngDestroy生命周期中使用unsubscribe

class SampleComponent implements OnInit, OnDestroy {
  private subscriptions: Subscription;
  private sampleObservable$: Observable<any>;

  constructor () {}

  ngOnInit(){
    this.subscriptions = this.sampleObservable$.subscribe( ... );
  }

  ngOnDestroy() {
    this.subscriptions.unsubscribe();
  }
}

在Rxjs中使用takeUntil

class SampleComponent implements OnInit, OnDestroy {
  private unsubscribe$: new Subject<void>;
  private sampleObservable$: Observable<any>;

  constructor () {}

  ngOnInit(){
    this.subscriptions = this.sampleObservable$
    .pipe(takeUntil(this.unsubscribe$))
    .subscribe( ... );
  }

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

你在ngOnInit调用的一些动作,在组件init时只会发生一次。

class SampleComponent implements OnInit {

  private sampleObservable$: Observable<any>;

  constructor () {}

  ngOnInit(){
    this.subscriptions = this.sampleObservable$
    .pipe(take(1))
    .subscribe( ... );
  }
}

我们也有async管道。但是,这个是在模板上使用的(不是在Angular组件中)。