1. ホーム
  2. angular

[解決済み] Angular/RxJS `Subscription` からいつ退会すればいいのか?

2022-03-19 04:24:13

質問

をいつ格納すればいいのでしょうか? Subscription インスタンスを起動し unsubscribe() の間に ngOnDestroy また、どのような場合に無視できるのでしょうか?

すべてのサブスクリプションを保存すると、コンポーネントコードに多くの混乱が生じます。

HTTPクライアントガイド は、このようなサブスクリプションを無視します。

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

同時に ルート&ナビガイド と書かれています。

最終的には、どこかに移動する。ルーターはこのコンポーネントをDOMから削除し、破壊する。そうなる前に、自分たちの後始末をする必要があります。具体的には、Angularがコンポーネントを破壊する前にサブスクライブを解除する必要があります。そうしないと、メモリリークが発生する可能性があります。

を解除します。 ObservablengOnDestroy メソッドを使用します。

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

解決方法は?

TL;DR

この質問では、2種類のObservablesがあります。 有限 値と 無限 の値を指定します。

http オブザーバブルの生成 有限 (1) 値を生成し、DOMイベントリスナーObservableのようなものは 無限 の値を指定します。

を手動で呼び出すと subscribe (非同期パイプを使用しない)場合は unsubscribe から 無限 オブザーバブルです。

を気にしないでください。 有限 は、RxJsが処理します。


ソースはこちら

  1. AngularのGitterにあるRob Wormaldの回答を探し出しました。 こちら .

    と述べている(わかりやすくするために再編成し、強調は私が行っている)。

    もしその 単一値列 (http リクエストのような) は 手動クリーンアップが不要 (コントローラで手動でサブスクライブしていると仮定して)

    と言うべきでしょう。 を完了するシーケンス httpのような単一値シーケンスはその一つです。

    無限列の場合 , 退会してください 非同期パイプはあなたのためにそれを行う

    また、彼は このYouTubeのビデオ オブザーバブルに関する "彼らは自分たちの後始末をする..."。 という文脈で、Observablesは コンプリート (プロミスのように、常に1つの値を生成して終了するので、常に完了します。XHRイベントリスナーをクリーンアップするために、プロミスから購読を解除することを心配したことはありませんよね?)

  2. また Angular 2のラングルガイド と書かれています。

    ほとんどの場合、明示的に unsubscribe メソッドは、早期にキャンセルする場合や Observable は、サブスクリプションよりも長い寿命を持ちます。のデフォルトの動作は Observable 演算子は、サブスクリプションを破棄するために .complete() または .error() メッセージはパブリッシュされます。RxJSは、ほとんどの場合、quot;fire and forget"方式で使用されるように設計されていることに留意してください。

    というフレーズは、どのような場合に使われるのでしょうか? 私たちの Observable は、購読期間よりも長い寿命があります" が適用されますか?

    サブスクリプションがコンポーネント内で作成され、Observableが完了する前に(あるいは「ずっと」前に)破壊された場合に適用されます。

    これを読むと、もし私たちが http リクエストまたは 10 個の値を出力する Observable を作成し、その前にコンポーネントが破壊される場合 http が返ってくるか、10個の値が放出されれば、まだOKです!

    リクエストが戻るか、10個目の値が最終的に発行されると、Observableは完了し、すべてのリソースがクリーンアップされます。

  3. もし この例 へのサブスクリプションは、同じRangleガイドから見ることができます。 route.params が必要です。 unsubscribe() というのも、これらの params が変化しなくなる(新しい値を出す)。

    この場合、ルートパラメータはまだ変化している可能性が高く(技術的にはアプリが終了するまで変化する可能性があります)、サブスクリプションで割り当てられたリソースはまだ割り当てられています。 完了 .

  4. この動画 NgEuropeのRob WormaldもRouter Observablesの配信を停止する必要はないと言っています。また、彼は http サービスや ActivatedRoute.params この動画 2016年11月より

  5. Angularのチュートリアルです。 ルーティング編 が以下のように記載されるようになりました。

    Router は、提供する observable を管理し、サブスクリプションをローカライズします。サブスクリプションは、コンポーネントが破棄されるときにクリーンアップされ、メモリリークから保護されるので、ルートから退会する必要はありません。 params Observable .

    以下は ディスカッション にて、Ward BellがRouter Observablesに関する説明を作成中であることを述べています。


私はNGConfでこの質問についてWard Bellと話をしました(彼が正しいと言ったこの答えも見せました)。しかし彼は、Angularのドキュメントチームがこの質問に対する解決策を持っていて、それは未発表であると言いました(彼らはそれを承認するために作業中ですが)。彼はまた、近々発表される公式な勧告で私のSOの答えを更新することができると言いました。

今後、私たち全員が使うべき解決法は private ngUnsubscribe = new Subject(); フィールドを持つすべてのコンポーネントに .subscribe() の呼び出しは、クラスコード内のObservablesの呼び出しです。

次に this.ngUnsubscribe.next(); this.ngUnsubscribe.complete(); の中で ngOnDestroy() メソッドを使用します。

隠し味は(既に指摘されているように メタメーカー を呼び出すことです。 takeUntil(this.ngUnsubscribe) の前に、それぞれの .subscribe() を呼び出すことで、コンポーネントが破棄されたときにすべてのサブスクリプションがクリーンアップされることを保証します。

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

を追加することが重要です。 takeUntil 演算子を最後の演算子として使用することで、演算子チェーン内の中間Observableによるリークを防ぐことができます。


最近だと Angularの冒険 Ben LeshとWard Bellは、コンポーネントでいつ、どのように購読を解除するかという問題について議論しています。この議論は1:05:30あたりから始まります。

ウォードが言及するのは 今、多くの機械を必要とするひどいテイクアンティルダンスがある"。 とShai Reznikが言及しています。 Angularはhttpやルーティングのようなサブスクリプションの一部を処理します。 .

それに対してBenは、AngularコンポーネントのライフサイクルイベントにObservablesをフックさせるための議論が現在行われていることに触れ、Wardは、コンポーネントの内部状態として維持されているObservablesをいつ完了するかを知る方法として、コンポーネントが購読できるライフサイクルイベントのObservableを提案しています。

とはいえ、今はほとんどソリューションが必要なので、他のリソースをいくつか紹介します。

  1. への提言 takeUntil() RxJsのコアチームメンバーであるNicholas Jamiesonによるパターン、およびそれを強制するためのTSLintルールです。 https://ncjamieson.com/avoiding-takeuntil-leaks/

  2. コンポーネントインスタンス ( this の間に自動的に配信を停止します。 ngOnDestroy : https://github.com/NetanelBasal/ngx-take-until-destroy

  3. AOTビルドをしないのであれば、人間工学的に少し優れた上記の別のバリエーション(しかし、我々は今、すべてAOTを行うべきである)。 https://github.com/smnbbrv/ngx-rx-collector

  4. カスタムディレクティブ *ngSubscribe は非同期パイプのように動作しますが、テンプレート内に埋め込みビューを作成するため、テンプレート全体で「アンラップ」された値を参照することができます。 https://netbasal.com/diy-subscription-handling-directive-in-angular-c8f6e762697f

ニコラスさんのブログへのコメントで、使いすぎのことを書いていますが takeUntil() は、コンポーネントがあまりにも多くのことを行おうとしている兆候であり、既存のコンポーネントを 特徴 プレゼンテーション コンポーネントを検討する必要があります。その上で | async Feature コンポーネントから Observable を Input というのは、Presentationalコンポーネントのサブスクリプションがどこにも必要ないことを意味します。このアプローチについてもっと読む こちら .