首页 文章

Angular / RxJs什么时候应该取消订阅“订阅”

提问于
浏览
490

我何时应该存储 Subscription 实例并在NgOnDestroy生命周期中调用 unsubscribe() 什么时候可以忽略它们?

保存所有订阅会在组件代码中引入很多混乱 .

HTTP Client Guide忽略这样的订阅:

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

在同一时间Route & Navigation Guide说:

最终,我们会在其他地方导航 . 路由器将从DOM中删除此组件并将其销毁 . 在此之前我们需要自己清理 . 具体来说,我们必须在Angular破坏组件之前取消订阅 . 如果不这样做可能会造成内存泄漏 . 我们在ngOnDestroy方法中取消订阅我们的Observable .

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();
}

14 回答

  • 42

    您不需要手动进行大量订阅和取消订阅 . 使用RxJS.SubjecttakeUntil combo来处理像boss这样的订阅:

    import {Subject} from "rxjs/Subject";
    
    @Component(
        {
            moduleId: __moduleName,
            selector: 'my-view',
            templateUrl: '../views/view-route.view.html',
        }
    )
    export class ViewRouteComponent implements OnDestroy
    {
        componentDestroyed$: Subject<boolean> = new Subject();
    
        constructor(protected titleService: TitleService)
        {
            this.titleService.emitter1$
                .takeUntil(this.componentDestroyed$)
                .subscribe(
                (data: any) =>
                {
                    // ... do something 1
                }
            );
    
            this.titleService.emitter2$
                .takeUntil(this.componentDestroyed$)
                .subscribe(
                (data: any) =>
                {
                    // ... do something 2
                }
            );
    
            // ...
    
            this.titleService.emitterN$
                .takeUntil(this.componentDestroyed$)
                .subscribe(
                (data: any) =>
                {
                    // ... do something N
                }
            );
        }
    
        ngOnDestroy()
        {
            this.componentDestroyed$.next(true);
            this.componentDestroyed$.complete();
        }
    }
    

    Alternative approach ,建议by @acumartini in comments,使用takeWhile而不是takeUntil . 您可能更喜欢它,但请注意,这样您的Observable执行将不会在组件的ngDestroy上被取消(例如,当您花费大量时间计算或等待来自服务器的数据时) . 方法,基于takeUntil,没有这个缺点,导致立即取消请求 . Thanks to @AlexChe for detailed explanation in comments .

    所以这是代码:

    @Component(
        {
            moduleId: __moduleName,
            selector: 'my-view',
            templateUrl: '../views/view-route.view.html',
        }
    )
    export class ViewRouteComponent implements OnDestroy
    {
        alive: boolean = true;
    
        constructor(protected titleService: TitleService)
        {
            this.titleService.emitter1$
                .takeWhile(() => this.alive)
                .subscribe(
                (data: any) =>
                {
                    // ... do something 1
                }
            );
    
            this.titleService.emitter2$
                .takeWhile(() => this.alive)
                .subscribe(
                (data: any) =>
                {
                    // ... do something 2
                }
            );
    
            // ...
    
            this.titleService.emitterN$
                .takeWhile(() => this.alive)
                .subscribe(
                (data: any) =>
                {
                    // ... do something N
                }
            );
        }
    
        // Probably, this.alive = false MAY not be required here, because
        // if this.alive === undefined, takeWhile will stop. I
        // will check it as soon, as I have time.
        ngOnDestroy()
        {
            this.alive = false;
        }
    }
    
  • 6

    Some of the best practices regarding observables unsubscriptions inside Angular components:

    来自Routing & Navigation的报价

    订阅组件中的observable时,您几乎总是安排在组件被销毁时取消订阅 . 有一些特殊的可观察物,这是不必要的 . ActivatedRoute可观察量是例外 . ActivatedRoute及其可观察对象与路由器本身绝缘 . 路由器在不再需要时会销毁路由组件,并且注入的ActivatedRoute会随之消失 . 无论如何,请随意取消订阅 . 它是无害的,从来都不是一种糟糕的做法 .

    并在回应以下链接:

    我收集了一些关于Angular组件中可观察的取消订阅的最佳实践,以便与您分享:

    • http 可观察的取消订阅是有条件的,我们应该考虑在逐个销毁组件之后运行'subscribe callback'的效果 . 我们知道有角度取消订阅并清理 http observable本身(1)(2) . 虽然从资源的角度来看这是真实的,但它只讲述了一半的故事 . 让's say we'谈论直接从组件中调用 http ,并且 http 响应花费的时间比需要的时间长,因此用户关闭了组件 . 即使组件已关闭并销毁,仍将调用 subscribe() 处理程序 . 这可能会产生不必要的副作用,在更糟糕的情况下会使应用程序状态中断 . 如果回调中的代码试图调用刚处理掉的东西,它也会导致异常 . 然而,有时他们是偶然的 . 比如,让's say you'重新创建一个电子邮件客户端,并在电子邮件发送完成后触发声音 - 即使组件已关闭(8),您仍然希望发生这种情况 .

    • 无需取消订阅完成或错误的可观察对象 . 但是,这样做没有害处(7) .

    • 尽可能使用 AsyncPipe 因为它会自动取消订阅组件销毁时的observable .

    • 取消订阅 ActivatedRoute observables,如 route.params ,如果它们是在嵌套(在组件选择器中添加内部tpl)或动态组件内订阅的,因为只要父/主机组件存在,它们可能会多次订阅 . 无需在Routing & Navigation docs上面的引用中提到的其他方案中取消订阅它们 .

    • 取消订阅通过Angular服务公开的组件之间共享的全局可观察对象,例如,只要组件初始化,它们可能会多次订阅 .

    • 无需取消订阅应用程序作用域服务的内部可观察量,因为此服务永远不会被销毁,没有真正的理由取消订阅它并且没有内存泄漏的可能性 . (6) .

    Note: 关于作用域服务,即组件提供程序,它们在销毁组件时被销毁 . 在这种情况下,如果我们订阅了这个提供程序中的任何observable,我们应该考虑使用OnDestroy生命周期钩子取消订阅,根据文档销毁服务时将调用它 .

    • 使用抽象技术可以避免因取消订阅而导致的任何代码混乱 . 您可以使用 takeUntil (3)管理您的订阅,也可以使用(4) The easiest way to unsubscribe from Observables in Angular中提到的 npm package .

    • 始终取消订阅 FormGroupform.valueChangesform.statusChanges 这样的可观测量

    • 始终取消订阅 Renderer2 服务的可观察量,如 renderer2.listen

    • 取消订阅其他可观察的其他内容作为内存泄漏防护步骤,直到Angular Docs明确告诉我们哪些observable不需要取消订阅(检查问题:(5) Documentation for RxJS Unsubscribing (Open)) .

    • Bonus:始终使用Angular方法绑定 HostListener 之类的事件,因为如果需要,可以很好地删除事件侦听器,并防止因事件绑定而导致的任何潜在内存泄漏 .

    A nice final tip :如果您不知道是否自动取消订阅/已完成observable,请将 complete 回调添加到 subscribe(...) 并检查是否在销毁该组件时调用它 .

  • 14

    由于seangwright的解决方案(编辑3)似乎非常有用,我还发现将此功能打包到基本组件中很麻烦,并且暗示其他项目团队成员记得在ngOnDestroy上调用super()来激活此功能 .

    这个答案提供了一种从超级调用中解脱出来的方法,并使“componentDestroyed $”成为基本组件的核心 .

    class BaseClass {
        protected componentDestroyed$: Subject<void> = new Subject<void>();
        constructor() {
    
            /// wrap the ngOnDestroy to be an Observable. and set free from calling super() on ngOnDestroy.
            let _$ = this.ngOnDestroy;
            this.ngOnDestroy = () => {
                this.componentDestroyed$.next();
                this.componentDestroyed$.complete();
                _$();
            }
        }
    
        /// placeholder of ngOnDestroy. no need to do super() call of extended class.
        ngOnDestroy() {}
    }
    

    然后您可以自由使用此功能,例如:

    @Component({
        selector: 'my-thing',
        templateUrl: './my-thing.component.html'
    })
    export class MyThingComponent extends BaseClass implements OnInit, OnDestroy {
        constructor(
            private myThingService: MyThingService,
        ) { super(); }
    
        ngOnInit() {
            this.myThingService.getThings()
                .takeUntil(this.componentDestroyed$)
                .subscribe(things => console.log(things));
        }
    
        /// optional. not a requirement to implement OnDestroy
        ngOnDestroy() {
            console.log('everything works as intended with or without super call');
        }
    
    }
    
  • 67

    正式的编辑#3答案(和变体)运作良好,但让我感觉到的是围绕可观察订阅的业务逻辑的“混乱” .

    这是使用包装器的另一种方法 .

    Warining:实验代码

    文件subscribeAndGuard.ts用于创建一个新的Observable扩展来包装 .subscribe() 并在其中包装 ngOnDestroy() .
    用法与 .subscribe() 相同,但引用该组件的其他第一个参数除外 .

    import { Observable } from 'rxjs/Observable';
    import { Subscription } from 'rxjs/Subscription';
    
    const subscribeAndGuard = function(component, fnData, fnError = null, fnComplete = null) {
    
      // Define the subscription
      const sub: Subscription = this.subscribe(fnData, fnError, fnComplete);
    
      // Wrap component's onDestroy
      if (!component.ngOnDestroy) {
        throw new Error('To use subscribeAndGuard, the component must implement ngOnDestroy');
      }
      const saved_OnDestroy = component.ngOnDestroy;
      component.ngOnDestroy = () => {
        console.log('subscribeAndGuard.onDestroy');
        sub.unsubscribe();
        // Note: need to put original back in place
        // otherwise 'this' is undefined in component.ngOnDestroy
        component.ngOnDestroy = saved_OnDestroy;
        component.ngOnDestroy();
    
      };
    
      return sub;
    };
    
    // Create an Observable extension
    Observable.prototype.subscribeAndGuard = subscribeAndGuard;
    
    // Ref: https://www.typescriptlang.org/docs/handbook/declaration-merging.html
    declare module 'rxjs/Observable' {
      interface Observable<T> {
        subscribeAndGuard: typeof subscribeAndGuard;
      }
    }
    

    这是一个包含两个订阅的组件,一个包含包装器,另一个包含订阅包 . 唯一需要注意的是 must implement OnDestroy (如果需要,还有空体),否则Angular不知道调用包装版本 .

    import { Component, OnInit, OnDestroy } from '@angular/core';
    import { Observable } from 'rxjs/Observable';
    import 'rxjs/Rx';
    import './subscribeAndGuard';
    
    @Component({
      selector: 'app-subscribing',
      template: '<h3>Subscribing component is active</h3>',
    })
    export class SubscribingComponent implements OnInit, OnDestroy {
    
      ngOnInit() {
    
        // This subscription will be terminated after onDestroy
        Observable.interval(1000)
          .subscribeAndGuard(this,
            (data) => { console.log('Guarded:', data); },
            (error) => { },
            (/*completed*/) => { }
          );
    
        // This subscription will continue after onDestroy
        Observable.interval(1000)
          .subscribe(
            (data) => { console.log('Unguarded:', data); },
            (error) => { },
            (/*completed*/) => { }
          );
      }
    
      ngOnDestroy() {
        console.log('SubscribingComponent.OnDestroy');
      }
    }
    

    一个演示插件是here

    An additional note: Re Edit 3 - 'Official'解决方案,这可以通过在订阅之前使用takeWhile()而不是takeUntil()来简化,并且在ngOnDestroy中使用简单的布尔值而不是另一个Observable .

    @Component({...})
    export class SubscribingComponent implements OnInit, OnDestroy {
    
      iAmAlive = true;
      ngOnInit() {
    
        Observable.interval(1000)
          .takeWhile(() => { return this.iAmAlive; })
          .subscribe((data) => { console.log(data); });
      }
    
      ngOnDestroy() {
        this.iAmAlive = false;
      }
    }
    
  • 18

    这取决于 . 如果通过调用 someObservable.subscribe() ,您开始占用一些必须在组件的生命周期结束时手动释放的资源,那么您应该调用 theSubscription.unsubscribe() 以防止内存泄漏 .

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

    getHero() 返回 http.get() 的结果 . 如果你看一下angular 2 source codehttp.get() 会创建两个事件监听器:

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

    通过调用 unsubscribe() ,您可以取消请求以及侦听器:

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

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

    通常情况下,这足以保证手动拨打电话 . 但根据WHATWG specXMLHttpRequest() 一旦被"done"进行垃圾收集,即使附加了事件监听器也是如此 . 所以我想这就是为什么angular 2官方指南省略 unsubscribe() 并让GC清理听众 .

    至于你的第二个例子,它取决于 params 的实现 . 截至今天,角度官方指南不再显示取消订阅 params . 我再次调查了src,发现 params 只是一个BehaviorSubject . 由于没有使用事件侦听器或计时器,并且没有创建全局变量,因此省略 unsubscribe() 应该是安全的 .

    您的问题的底线是始终将 unsubscribe() 调用为防止内存泄漏,除非您确定observable的执行不会创建全局变量,添加事件侦听器,设置计时器或执行任何其他导致内存的操作泄漏 .

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

  • 4

    Angular 2官方文档提供了何时取消订阅以及何时可以安全忽略的说明 . 看看这个链接:

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

    查找 Headers 为 Parent and children communicate via a service 的段落,然后是蓝色框:

    请注意,我们捕获订阅并在AstronautComponent销毁时取消订阅 . 这是一个内存泄漏保护步骤 . 此应用程序中没有实际风险,因为AstronautComponent的生命周期与应用程序本身的生命周期相同 . 在更复杂的应用程序中,这并非总是如此 . 我们不会将此保护添加到MissionControlComponent,因为作为父级,它控制MissionService的生命周期 .

    我希望这可以帮助你 .

  • 3

    我试过seangwright的解决方案(编辑3)

    这对于由计时器或间隔创建的Observable不起作用 .

    但是,我通过使用另一种方法使其工作:

    import { Component, OnDestroy, OnInit } from '@angular/core';
    import 'rxjs/add/operator/takeUntil';
    import { Subject } from 'rxjs/Subject';
    import { Subscription } from 'rxjs/Subscription';
    import 'rxjs/Rx';
    
    import { MyThingService } from '../my-thing.service';
    
    @Component({
       selector: 'my-thing',
       templateUrl: './my-thing.component.html'
    })
    export class MyThingComponent implements OnDestroy, OnInit {
       private subscriptions: Array<Subscription> = [];
    
      constructor(
         private myThingService: MyThingService,
       ) { }
    
      ngOnInit() {
        const newSubs = this.myThingService.getThings()
            .subscribe(things => console.log(things));
        this.subscriptions.push(newSubs);
      }
    
      ngOnDestroy() {
        for (const subs of this.subscriptions) {
          subs.unsubscribe();
       }
     }
    }
    
  • 3

    您通常需要在组件被销毁时取消订阅,但Angular将会越来越多地处理它,例如在新的Angular4的次要版本,他们有此部分用于路由取消订阅:

    你需要取消订阅吗?如ActivatedRoute:路由和导航页面的路径信息的一站式服务部分所述,路由器管理它提供的可观察量并本地化订阅 . 在销毁组件时清除订阅,防止内存泄漏,因此您无需取消订阅paramMap Observable路由 .

    另外下面的例子是一个很好的例子,从Angular创建一个组件并在之后销毁它,看看组件如何实现OnDestroy,如果你需要onInit,你也可以在你的组件中实现它,比如implements OnInit, OnDestroy

    import { Component, Input, OnDestroy } from '@angular/core';  
    import { MissionService } from './mission.service';
    import { Subscription }   from 'rxjs/Subscription';
    
    @Component({
      selector: 'my-astronaut',
      template: `
        <p>
          {{astronaut}}: <strong>{{mission}}</strong>
          <button
            (click)="confirm()"
            [disabled]="!announced || confirmed">
            Confirm
          </button>
        </p>
      `
    })
    
    export class AstronautComponent implements OnDestroy {
      @Input() astronaut: string;
      mission = '<no mission announced>';
      confirmed = false;
      announced = false;
      subscription: Subscription;
    
      constructor(private missionService: MissionService) {
        this.subscription = missionService.missionAnnounced$.subscribe(
          mission => {
            this.mission = mission;
            this.announced = true;
            this.confirmed = false;
        });
      }
    
      confirm() {
        this.confirmed = true;
        this.missionService.confirmMission(this.astronaut);
      }
    
      ngOnDestroy() {
        // prevent memory leak when component destroyed
        this.subscription.unsubscribe();
      }
    }
    
  • 2

    基于:Using Class inheritance to hook to Angular 2 component lifecycle

    另一种通用方法:

    export abstract class UnsubscribeOnDestroy implements OnDestroy {
      protected d$: Subject<any>;
    
      constructor() {
        this.d$ = new Subject<void>();
    
        const f = this.ngOnDestroy;
        this.ngOnDestroy = () => {
          f();
          this.d$.next();
          this.d$.complete();
        };
      }
    
      public ngOnDestroy() {
        // no-op
      }
    
    }
    

    并使用:

    @Component({
        selector: 'my-comp',
        template: ``
    })
    export class RsvpFormSaveComponent extends UnsubscribeOnDestroy implements OnInit {
    
        constructor() {
            super();
        }
    
        ngOnInit(): void {
          Observable.of('bla')
          .takeUntil(this.d$)
          .subscribe(val => console.log(val));
        }
    }
    
  • 1

    ---编辑4 - 其他资源(2018/09/01)

    在最近的一集Adventures in Angular中,Ben Lesh和Ward Bell讨论了如何/何时取消订阅组件的问题 . 讨论从大约1:05:30开始 .

    沃德提到 right now there's an awful takeUntil dance that takes a lot of machinery 和Shai Reznik提到 Angular handles some of the subscriptions like http and routing .

    作为回应,Ben提到现在正在进行讨论以允许Observables挂钩Angular组件生命周期事件,而Ward建议生命周期事件的Observable,组件可以订阅这些事件以了解何时完成作为组件内部状态维护的Observable .

    也就是说,我们现在主要需要解决方案,所以这里有一些其他资源 .

    我在对Nicholas博客的评论中提到过度使用 takeUntil() 可能表明你的组件试图做太多,并且应该考虑将现有组件分成 FeaturePresentational 组件 . 然后,您可以将功能组件中的Observable | async 显示为演示组件的 Input ,这意味着在任何地方都不需要订阅 . 阅读有关此方法的更多信息here

    ---编辑3 - '官方'解决方案(2017/04/09)

    我在NGConf与Ward Bell谈到了这个问题(我甚至向他展示了这个答案,他说这是正确的)但是他告诉我Angular的文档团队解决了这个未发表的问题(虽然他们正在努力让它获得批准) ) . 他还告诉我,我可以通过即将发布的官方建议更新我的答案 .

    我们今后应该使用的解决方案是将 private ngUnsubscribe = new Subject(); 字段添加到在其类代码中对 Observable 进行 .subscribe() 调用的所有组件 .

    然后我们在 ngOnDestroy() 方法中调用 this.ngUnsubscribe.next(); this.ngUnsubscribe.complete(); .

    秘密酱(正如@metamaker所述)是在我们的每个 .subscribe() 调用之前调用 takeUntil(this.ngUnsubscribe) ,这将保证在组件被销毁时清除所有订阅 .

    例:

    import { Component, OnDestroy, OnInit } from '@angular/core';
    // RxJs 6.x+ import paths
    import { filter, startWith, takeUntil } from 'rxjs/operators';
    import { Subject } from 'rxjs';
    import { BookService } from '../books.service';
    
    @Component({
        selector: 'app-books',
        templateUrl: './books.component.html'
    })
    export class BooksComponent implements OnDestroy, OnInit {
        private ngUnsubscribe = new Subject();
    
        constructor(private booksService: BookService) { }
    
        ngOnInit() {
            this.booksService.getBooks()
                .pipe(
                   startWith([]),
                   filter(books => books.length > 0),
                   takeUntil(this.ngUnsubscribe)
                )
                .subscribe(books => console.log(books));
    
            this.booksService.getArchivedBooks()
                .pipe(takeUntil(this.ngUnsubscribe))
                .subscribe(archivedBooks => console.log(archivedBooks));
        }
    
        ngOnDestroy() {
            this.ngUnsubscribe.next();
            this.ngUnsubscribe.complete();
        }
    }
    

    Note:takeUntil 运算符添加为最后一个运算符非常重要,以防止运算符链中的中间可观察对象泄漏 .

    ---编辑2(2016/12/28)

    Source 5

    Angular教程,路由章节现在声明如下:"The Router manages the observables it provides and localizes the subscriptions. The subscriptions are cleaned up when the component is destroyed, protecting against memory leaks, so we don't need to unsubscribe from the route params Observable." - Mark Rajcok

    这是关于路由器可观测量的Angular文档的Github问题的discussion,其中Ward Bell提到所有这些的澄清正在进行中 .

    ---编辑1

    Source 4

    在这个video from NgEurope中,Rob Wormald还说你不需要取消订阅Router Observables . 他还提到 http 服务和 ActivatedRoute.params 在这个video from November 2016 .

    ---原始答案

    TLDR:

    对于这个问题,有(2)种 Observables - finite 值和 infinite 值 .

    http Observables 产生 finite (1)值和类似DOM event listener Observables 产生 infinite 的值 .

    如果您手动调用 subscribe (不使用异步管道),则 unsubscribe 来自 infinite Observables .

    不要担心 finiteRxJs 会照顾他们 .

    Source 1

    我在Angular的Gitter here中找到了Rob Wormald的回答 .

    他表示(我为了清晰而重组,重点是我的)

    如果它是单值序列(如http请求),则手动清理是不必要的(假设您手动在控制器中订阅)我应该说“如果它的序列完成”(其中单个值序列,一个la http) ,是一个)如果它是一个无限序列,你应该取消订阅哪个异步管道为你做

    此外,他在Observables中提及 they clean up after themselves ......在Observables的背景下 complete (像Promises一样,因为它们总是产生1个值并结束 - 我们从不担心取消订阅Promises以确保它们清理 xhr 事件监听器,对吗?) .

    Source 2

    它也在Rangle guide to Angular 2中读取

    在大多数情况下,我们不需要显式调用unsubscribe方法,除非我们想要提前取消或我们的Observable比我们的订阅具有更长的寿命 . Observable运算符的默认行为是在发布.complete()或.error()消息后立即处理订阅 . 请记住,RxJS的设计大多数时候都是以“一见不醒”的方式使用 .

    什么时候短语 our Observable has a longer lifespan than our subscription 适用?

    当在 Observable 完成之前(或之前'long'之前)销毁的组件内创建订阅时,它适用 .

    如果我们订阅了一个 http 请求或一个发出10个值的observable并且在 http 请求返回之前销毁了我们的组件或者已经发出了10个值,那么我认为这意味着我们仍然可以!

    当请求返回或最终发出第10个值时, Observable 将完成,所有资源将被清除 .

    Source 3

    如果我们从同一个Rangle指南中查看this example,我们可以看到 Subscriptionroute.params 确实需要 unsubscribe() ,因为我们不知道 params 何时会停止更改(发出新值) .

    可以通过导航来销毁该组件,在这种情况下,路由参数可能仍在改变(它们可能在技术上改变直到应用程序结束)并且仍然会分配订阅中分配的资源,因为没有 completion .

  • 1

    上述情况的另一个简短补充是:

    • 始终取消订阅,当订阅流中的新值不再需要或无关紧要时,在少数情况下会导致触发器数量减少和性能提升 . 诸如订阅数据/事件不再存在的组件或者需要对新的全新订阅的新订阅(刷新等)之类的情况是取消订阅的一个很好的示例 .
  • 3

    Subscription类有一个有趣的特性:

    表示一次性资源,例如执行Observable . 订阅有一个重要的方法,取消订阅,不带参数,只是处理订阅所持有的资源 . 此外,订阅可以通过add()方法组合在一起,该方法将子订阅附加到当前订阅 . 当订阅被取消订阅时,其所有子女(及其孙子女)也将被取消订阅 .

    您可以创建一个聚合订阅对象,该对象将您的所有订阅分组 . 您可以通过创建一个空的Subscription并使用其 add() 方法向其添加订阅来完成此操作 . 当您的组件被销毁时,您只需要取消订阅聚合订阅 .

    @Component({ ... })
    export class SmartComponent implements OnInit, OnDestroy {
      private subscriptions = new Subscription();
    
      constructor(private heroService: HeroService) {
      }
    
      ngOnInit() {
        this.subscriptions.add(this.heroService.getHeroes().subscribe(heroes => this.heroes = heroes));
        this.subscriptions.add(/* another subscription */);
        this.subscriptions.add(/* and another subscription */);
        this.subscriptions.add(/* and so on */);
      }
    
      ngOnDestroy() {
        this.subscriptions.unsubscribe();
      }
    }
    
  • 1

    @seangwright的答案之后,我在组件中订阅了've written an abstract class that handles 229360 observables':

    import { OnDestroy } from '@angular/core';
    import { Subscription } from 'rxjs/Subscription';
    import { Subject } from 'rxjs/Subject';
    import { Observable } from 'rxjs/Observable';
    import { PartialObserver } from 'rxjs/Observer';
    
    export abstract class InfiniteSubscriberComponent implements OnDestroy {
      private onDestroySource: Subject<any> = new Subject();
    
      constructor() {}
    
      subscribe(observable: Observable<any>): Subscription;
    
      subscribe(
        observable: Observable<any>,
        observer: PartialObserver<any>
      ): Subscription;
    
      subscribe(
        observable: Observable<any>,
        next?: (value: any) => void,
        error?: (error: any) => void,
        complete?: () => void
      ): Subscription;
    
      subscribe(observable: Observable<any>, ...subscribeArgs): Subscription {
        return observable
          .takeUntil(this.onDestroySource)
          .subscribe(...subscribeArgs);
      }
    
      ngOnDestroy() {
        this.onDestroySource.next();
        this.onDestroySource.complete();
      }
    }
    

    要使用它,只需在角度组件中扩展它并按如下方式调用 subscribe() 方法:

    this.subscribe(someObservable, data => doSomething());
    

    它也像往常一样接受错误和完整的回调,一个观察者对象,或者根本不接受回调 . 如果您还在子组件中实现该方法,请记得调用 super.ngOnDestroy() .

    在这里找到Ben Lesh的另一个参考:RxJS: Don’t Unsubscribe .

  • 746

    我喜欢最后两个答案,但如果子类在 ngOnDestroy 中引用 "this" ,我遇到了一个问题 .

    我修改它是这样,看起来它解决了这个问题 .

    export abstract class BaseComponent implements OnDestroy {
        protected componentDestroyed$: Subject<boolean>;
        constructor() {
            this.componentDestroyed$ = new Subject<boolean>();
            let f = this.ngOnDestroy;
            this.ngOnDestroy = function()  {
                // without this I was getting an error if the subclass had
                // this.blah() in ngOnDestroy
                f.bind(this)();
                this.componentDestroyed$.next(true);
                this.componentDestroyed$.complete();
            };
        }
        /// placeholder of ngOnDestroy. no need to do super() call of extended class.
        ngOnDestroy() {}
    }
    

相关问题