Angular/RxJs我应该何时取消订阅`

Angular/RxJs我应该何时取消订阅`,angular,rxjs,observable,subscription,angular-component-life-cycle,Angular,Rxjs,Observable,Subscription,Angular Component Life Cycle,在Ngondestory生命周期中,我应该何时存储订阅实例并调用取消订阅(),何时可以忽略它们 保存所有订阅会给组件代码带来很多混乱 忽略以下订阅: import { Component, OnDestroy, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; @Component({ template: '<div></div>' }) export class SomeComponen

在Ngondestory生命周期中,我应该何时存储
订阅
实例并调用
取消订阅()
,何时可以忽略它们

保存所有订阅会给组件代码带来很多混乱

忽略以下订阅:

import { Component, OnDestroy, OnInit } from '@angular/core';
import { Observable } from 'rxjs';

@Component({ template: '<div></div>' })
export class SomeComponent implements OnInit, OnDestroy {

  ngOnInit(): void {
    const observable = Observable.create(observer => {
      observer.next('Hello');
    });

    observable
      .pipe(takeUntilDestroyed(this))
      .subscribe(val => console.log(val));
  }

  ngOnDestroy(): void {
  }
}
getHeroes(){
this.heroService.getheros()
.订阅(
英雄=>这个。英雄=英雄,
error=>this.errorMessage=表示:

最终,我们将导航到其他地方。路由器将从DOM中删除此组件并销毁它。我们需要在这之前进行清理。具体来说,我们必须在Angular销毁该组件之前取消订阅。否则可能会造成内存泄漏

我们在
Ngondestory
方法中取消订阅我们的
Observable

private sub:any;
恩戈尼尼特(){
this.sub=this.route.params.subscribe(params=>{
让id=+params['id'];//(+)将字符串'id'转换为数字
this.service.getHero(id).then(hero=>this.hero=hero);
});
}
恩贡德斯特罗(){
此.sub.取消订阅();
}

Angular 2官方文档解释了何时取消订阅以及何时可以安全地忽略。请查看此链接:

查找标题为“家长与子女通过服务进行沟通”的段落,然后查找蓝色框:

请注意,我们捕获订阅并在组件销毁时取消订阅。这是一个内存泄漏保护步骤。此应用程序中没有实际风险,因为组件的生存期与应用程序本身的生存期相同。在更复杂的应用程序中,情况并非总是如此

我们不将此防护添加到MissionControlComponent,因为作为父级,它控制MissionService的生存期


我希望这对您有所帮助。

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

让我们仔细看看您的示例:

getHero()
返回
http.get()
的结果。如果查看angular 2,
http.get()
将创建两个事件侦听器:

_xhr.addEventListener('load', onLoad);
_xhr.addEventListener('error', onError);
_xhr.removeEventListener('load', onLoad);
_xhr.removeEventListener('error', onError);
_xhr.abort();
通过调用
unsubscribe()
,您可以取消请求以及侦听器:

_xhr.addEventListener('load', onLoad);
_xhr.addEventListener('error', onError);
_xhr.removeEventListener('load', onLoad);
_xhr.removeEventListener('error', onError);
_xhr.abort();
请注意,
\uxhr
是特定于平台的,但我认为可以安全地假设它是一个
XMLHttpRequest()

通常情况下,这足以证明手动调用
unsubscribe()
是正确的。但根据这一点,一旦
XMLHttpRequest()
完成,即使有事件侦听器附加到它,它也会被垃圾收集。所以我想这就是为什么angular 2官方指南省略了
unsubscribe()
并让GC清理侦听器

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

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

当有疑问时,查看可观察的实现。如果观察者已经将一些清理逻辑写入其<代码> un订阅()>代码>,这通常是构造函数返回的函数,那么您有充分的理由认真考虑调用<代码> un订阅()< <代码> > < /p> ---编辑4个附加资源。(2018/09/01) 在Ben Lesh和Ward Bell最近的一集中,他们讨论了如何/何时取消订阅组件的问题。讨论大约在1:05:30开始

Ward提到现在有一个可怕的takeUntil舞蹈,需要很多机器,Shai Reznik提到Angular处理一些订阅,比如http和路由

作为回应,Ben提到目前正在进行讨论,以允许可观察到的组件生命周期事件与角度组件生命周期事件挂钩,Ward建议组件可以订阅生命周期事件的可观察事件,作为了解何时完成作为组件内部状态维护的可观察事件的一种方式

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

  • RxJs核心团队成员Nicholas Jamieson对
    takeUntil()
    模式的建议以及帮助实施该模式的tslint规则

  • 轻量级npm包,公开一个可观察的操作符,该操作符将组件实例(
    this
    )作为参数,并在
    ngondestory
    期间自动取消订阅。

  • 如果您不进行AOT构建(但我们现在都应该进行AOT),则上述方法的另一个变体具有稍微更好的人体工程学。

  • 自定义指令
    *ngSubscribe
    ,其工作原理类似于异步管道,但会在模板中创建嵌入式视图,以便您可以在整个模板中引用“unwrapped”值。

  • 我在Nicholas博客的评论中提到,过度使用了
    takeUntil()
    可能表明您的组件试图做得太多,应考虑将现有组件分为功能组件和表示组件。然后,您可以
    |异步
    从功能组件中观察到的内容
    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() {}
    }
    
    @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;
      }
    }
    
    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();
      }
    }
    
    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();
      }
    }
    
    this.subscribe(someObservable, data => doSomething());
    
    import { Observable, Subject } from 'rxjs';
    import { takeUntil } from 'rxjs/operators';
    import { OnDestroy } from '@angular/core';
    
    export const takeUntilDestroyed = (componentInstance: OnDestroy) => <T>(observable: Observable<T>) => {
      const subjectPropertyName = '__takeUntilDestroySubject__';
      const originalOnDestroy = componentInstance.ngOnDestroy;
      const componentSubject = componentInstance[subjectPropertyName] as Subject<any> || new Subject();
    
      componentInstance.ngOnDestroy = (...args) => {
        originalOnDestroy.apply(componentInstance, args);
        componentSubject.next(true);
        componentSubject.complete();
      };
    
      return observable.pipe(takeUntil<T>(componentSubject));
    };
    
    import { Component, OnDestroy, OnInit } from '@angular/core';
    import { Observable } from 'rxjs';
    
    @Component({ template: '<div></div>' })
    export class SomeComponent implements OnInit, OnDestroy {
    
      ngOnInit(): void {
        const observable = Observable.create(observer => {
          observer.next('Hello');
        });
    
        observable
          .pipe(takeUntilDestroyed(this))
          .subscribe(val => console.log(val));
      }
    
      ngOnDestroy(): void {
      }
    }
    
    import {UserService} from './user.service';
    
    private user = {name: 'test', id: 1}
    
    constructor(public userService: UserService) {
        this.userService.onUserChange.next(this.user);
    }
    
    import {BehaviorSubject} from 'rxjs/BehaviorSubject';
    
    public onUserChange: BehaviorSubject<any> = new BehaviorSubject({});
    
    import {Subscription} from 'rxjs/Subscription';
    import {UserService} from './user.service';
    
    private onUserChange: Subscription;
    
    constructor(public userService: UserService) {
        this.onUserChange = this.userService.onUserChange.subscribe(user => {
            console.log(user);
        });
    }
    
    public ngOnDestroy(): void {
        // note: Here you have to be sure to unsubscribe to the subscribe item!
        this.onUserChange.unsubscribe();
    }
    
    export class Unsubscriber implements OnDestroy {
      private subscriptions: Subscription[] = [];
    
      addSubscription(subscription: Subscription | Subscription[]) {
        if (Array.isArray(subscription)) {
          this.subscriptions.push(...subscription);
        } else {
          this.subscriptions.push(subscription);
        }
      }
    
      unsubscribe() {
        this.subscriptions
          .filter(subscription => subscription)
          .forEach(subscription => {
            subscription.unsubscribe();
          });
      }
    
      ngOnDestroy() {
        this.unsubscribe();
      }
    }
    
    class SampleComponent extends Unsubscriber {
        constructor () {
            super();
        }
    
        this.addSubscription(subscription);
    }
    
     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());
      }
    
    subscriptions = new Subscription();
    
    this.subscriptions.add(subscribeOne);
    this.subscriptions.add(subscribeTwo);
    
    ngOnDestroy() {
      this.subscriptions.unsubscribe();
    }
    
    if (this.closed) {
      return;
    }
    
    const predicate = (result: any) => { 
      // check value and return true if it is the result that satisfies your needs
      return true;
    }
    observable.pipe(first(predicate)).subscribe(observer);
    
    observable.pipe(single()).subscribe(observer);
    
    @Component({...})
    export class AppComponent implements OnInit, OnDestroy {
        subscription: Subscription 
        ngOnInit () {
            var observable = Rx.Observable.interval(1000);
            this.subscription = observable.subscribe(x => console.log(x));
        }
        ngOnDestroy() {
            this.subscription.unsubscribe()
        }
    }
    
    @Component({...})
    export class AppComponent implements OnInit, OnDestroy {
        subscription1$: Subscription
        subscription2$: Subscription 
        ngOnInit () {
            var observable1$ = Rx.Observable.interval(1000);
            var observable2$ = Rx.Observable.interval(400);
            this.subscription1$ = observable.subscribe(x => console.log("From interval 1000" x));
            this.subscription2$ = observable.subscribe(x => console.log("From interval 400" x));
        }
        ngOnDestroy() {
            this.subscription1$.unsubscribe()
            this.subscription2$.unsubscribe()
        }
    }
    
    import { Subscription } from "rxjs";
    
    export class Subscriptor {
        private static subscriptions: Subscription[] = [];
    
        static set subscription(subscription: Subscription) {
            Subscriptor.subscriptions.push(subscription);
        }
    
        static unsubscribe() {
            Subscriptor.subscriptions.forEach(subscription => subscription ? subscription.unsubscribe() : 0);
            Subscriptor.subscriptions = [];
        }
    }
    
    ngOnInit(): void {
        Subscriptor.subscription = this.userService.getAll().subscribe(users => this.users = users);
        Subscriptor.subscription = this.categoryService.getAll().subscribe(categories => this.categories = categories);
        Subscriptor.subscription = this.postService.getAll().subscribe(posts => this.posts = posts);
    }
    
    ngOnDestroy(): void {
        Subscriptor.unsubscribe();
    }