Anglarの中でいつ購読をキャンセルしますか?


Observableオブジェクトを購読したり、イベントの傍受を設定したりする場合、ある時点で購読キャンセル操作を行い、さらにオペレーティングシステムのメモリを解放する必要があることを知っているかもしれません。そうでないと、あなたのアプリケーションにメモリが漏れる可能性があります。
次に、ハンドンデストロイライフサイクルフックにおいて、手動で購読キャンセル操作を実行するのによくある場面を見せてください。
リソースを手動で解放するシーン
フォーム

export class TestComponent {

 ngOnInit() {
  this.form = new FormGroup({...});
  //         
  this.valueChanges = this.form.valueChanges.subscribe(console.log);
  //                        
  this.statusChanges = this.form.statusChanges.subscribe(console.log);
 }

 ngOnDestroy() {
  this.valueChanges.unsubscribe();
  this.statusChanges.unsubscribe();
 }
}
上記のシナリオは他のフォームコントロールにも適用されます。
ルート

export class TestComponent {
 constructor(private route: ActivatedRoute, private router: Router) { }

 ngOnInit() {
  this.route.params.subscribe(console.log);
  this.route.queryParams.subscribe(console.log);
  this.route.fragment.subscribe(console.log);
  this.route.data.subscribe(console.log);
  this.route.url.subscribe(console.log);
  
  this.router.events.subscribe(console.log);
 }

 ngOnDestroy() {
  //            
 }
}

Rendererサービス

export class TestComponent {
 constructor(
  private renderer: Renderer2, 
  private element : ElementRef) { }

 ngOnInit() {
  this.click = this.renderer
    .listen(this.element.nativeElement, "click", handler);
 }

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

Infinite Observables
interval()またはfroomEvent()を使用すると、無限のObservableオブジェクトが作成されます。このようにすれば、私達はもうそれらを使う必要がない時、購読をキャンセルして、手動で資源を釈放する必要があります。

export class TestComponent {
 constructor(private element : ElementRef) { }

 interval: Subscription;
 click: Subscription;

 ngOnInit() {
  this.interval = Observable.interval(1000).subscribe(console.log);
  this.click = Observable.fromEvent(this.element.nativeElement, 'click')
              .subscribe(console.log);
 }

 ngOnDestroy() {
  this.interval.unsubscribe();
  this.click.unsubscribe();
 }
}
Redux Store

export class TestComponent {

 constructor(private store: Store) { }

 todos: Subscription;

 ngOnInit() {
   /**
   * select(key : string) {
   *  return this.map(state => state[key]).distinctUntilChanged();
   * }
   */
   this.todos = this.store.select('todos').subscribe(console.log); 
 }

 ngOnDestroy() {
  this.todos.unsubscribe();
 }
}
リソースシーンを手動で解放する必要はありません。
Aync Pipe

@Component({
 selector: 'test',
 template: `<todos [todos]="todos$ | async"></todos>`
})
export class TestComponent {
 constructor(private store: Store) { }
 
 ngOnInit() {
   this.todos$ = this.store.select('todos');
 }
}
コンポーネントが破壊されると、ASyncパイプは自動的に購読取消操作を行い、メモリ漏れのリスクを避ける。
Anglar AyncPipeソースのセグメント

@Pipe({name: 'async', pure: false})
export class AsyncPipe implements OnDestroy, PipeTransform {
 // ...
 constructor(private _ref: ChangeDetectorRef) {}

 ngOnDestroy(): void {
  if (this._subscription) {
   this._dispose();
  }
 }
}
@HostListener

export class TestDirective {
 @HostListener('click')
 onClick() {
  ....
 }
}
注意したいのは、@HostListener装飾器を使ってイベントの傍受を追加した場合、手動で購読をキャンセルすることができません。イベントの傍受を手動で削除する必要がある場合は、以下のような方法があります。

// subscribe
this.handler = this.renderer.listen('document', "click", event =>{...});

// unsubscribe
this.handler();
Finite Observable
HTTPサービスやtimer Observableオブジェクトを使用する場合は、手動で購読キャンセル操作を行う必要もありません。

export class TestComponent {
 constructor(private http: Http) { }

 ngOnInit() {
  //   1s    ,      
  Observable.timer(1000).subscribe(console.log);
  this.http.get('http://api.com').subscribe(console.log);
 }
}
timer操作子
操作子の署名

public static timer(initialDelay: number | Date, period: number, scheduler: Scheduler): Observable
オペレータの役割
timerは無限の自己増加数列を出すObservableを返します。一定の時間間隔があります。この間隔はあなたが選択します。
オペレータの例

//   1        ,3      
var numbers = Rx.Observable.timer(3000, 1000);
numbers.subscribe(x => console.log(x));

// 5        
var numbers = Rx.Observable.timer(5000);
numbers.subscribe(x => console.log(x));

最終提案
できるだけ少ない方法でunsubscribeを呼び出すべきです。RxJS:Don't Unisubscribeの記事でSubjectに関する詳細を知ることができます。
具体例は以下の通りです。

export class TestComponent {
 constructor(private store: Store) { }

 private componetDestroyed: Subject = new Subject();
 todos: Subscription;
 posts: Subscription;

 ngOnInit() {
   this.todos = this.store.select('todos')
           .takeUntil(this.componetDestroyed).subscribe(console.log); 
           
   this.posts = this.store.select('posts')
           .takeUntil(this.componetDestroyed).subscribe(console.log); 
 }

 ngOnDestroy() {
  this.componetDestroyed.next();
  this.componetDestroyed.unsubscribe();
 }
}
Tale Unitil操作子
操作子の署名

public takeUntil(notifier: Observable): Observable<T>
オペレータの役割
notifier Observableが値を出すまでソースObserbleの値を発行します。
オペレータの例

var interval = Rx.Observable.interval(1000);
var clicks = Rx.Observable.fromEvent(document, 'click');
var result = interval.takeUntil(clicks);

result.subscribe(x => console.log(x));
以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。