web-dev-qa-db-ja.com

監視可能なサブスクリプションのRxjs数

私のAngular 2アプリでは、多くのオブザーバブルとサブスクリプションがあります。もちろん、ページを離れるときにサブスクリプションを解除する必要がありますが、アクティブなサブスクリプションの数を取得できるかどうかを調べようとしています。デバッグ情報のため、または購読解除を忘れた場合。

そのような情報はrxjsで利用できますか?

12
Stefan Koenen

おそらく少し遅れますが、 _rxjs-spy_ の助けを借りることができます。

このソリューションは、提案されたソリューションと同等であり、私の意見では、より保守しやすくなっています。

私は通常、ファイアアンドフォーゲット戦略としてmain.tsでグローバルに有効にします。あなたは単にする必要があります:

  1. パッケージマネージャーを介してrxjs-spyをインストールします
  2. main.tsに作成関数への参照をインポートします:_import { create } from 'rxjs-spy';_
  3. angular初期化スニペットの直後にデバッグビルドでrxjs-spyを初期化します:

    _
    if (environment.production) {
        enableProdMode();
    }
    else {
        //we enable RXjs Spy on non production bulds only
        const spy = create();
        // we call show for two purposes: first is to log to the console an empty snapshot so we can see that everything is working as expected, then to suppress unused variable usage (the latter is a convention on mine)
        spy.show();
    }
    
    _
  4. オブザーバブルに名前を付けます。

    _
    import { tag } from 'rxjs-spy/operators';
    
    ...
    
    // This is a sample method which asks for a "Product" entity. Product and this.http is omitted as the focus is on tagging the observable
    public getProductById(productId: number): Observable<Product> {
        let params = new HttpParams()
            .append('productId', productId.toString())
            ;
        // we tag the returned observable with the name 'getProductById' (this is a convention on mine, you can choose whatsoever name)
        return this.http.get<Product>(this.baseUrl + "api/product", { params: params }).pipe(tag("getProductById"));
    }
    
    _
  5. rxjsの状態を確認する必要がある場合は、コンソールウィンドウを開き、rxSpy.show()を使用して現在のスナップショットを取得できます。

追加のコマンドを使用できます。非常に詳細なチュートリアルは rxjs Spyを使用したデバッグ です。

(この回答を読んでいる誰かが、リスト内に適切にフォーマットを含めることができないため、フォーマットを修正できれば非常に嬉しいです)

3
Pier

あなたの場合、refCountingでRxJSサブジェクトをうまく利用することができます。サブジェクトに監視可能なソースを提供し、refCountにサブスクリプションを管理させます。 refCountをリッスンしているオブザーバーがいない場合、refCountはソースobservableのサブスクライブを解除します。一方、オブザーバー数が0で、オブザーバーがサブスクライブしている場合は、ソースの新しいインスタンスを作成します。

サブジェクトは、オブザーバーとオブザーバブルのソースの間のプロキシとして機能し、オブザーバーのサブスクライブとサブスクライブ解除を管理します。本質的に、それがどのように機能するかは、最初のオブザーバーにサブジェクトをサブスクライブさせ、次にサブジェクトにソースオブザーバブルをサブスクライブさせる場合です(refCountは0から1になりました)。サブジェクトを使用すると、複数のオブザーバーがユニキャストソースオブザーバブルをリッスンしてマルチキャストできます。オブザーバーがサブスクライブ解除を開始し、refCountが再び0に低下すると、サブジェクト自体がソースオブザーバブルからサブスクライブ解除されます。

それはコードでよりよく理解されています:

_const {
  Observable
} = Rx;

let sourceObservable = Observable
  .create((observer) => {
    let count = 0;
    let interval = setInterval(() => {
      observer.next(count++)
    }, 700);

    setTimeout(() => {
      clearInterval(interval);
      observer.complete();
    }, 5500);

    return () => {
      clearInterval(interval);
      console.log('######## Source observable unsubscribed');
    }
  })
  .do((x) => console.log('#### Source emits: ' + x));

let subject = sourceObservable
  .share()
  //.do((x) => console.log('#### Subject emits: ' + x))
  ;

let pageOneObserver;
let pageTwoObserver;
let pageThreeObserver;

setTimeout(() => {
  console.log('pageOneObserver will subscribe');
  pageOneObserver = subject.subscribe({
    next: (x) => {
      console.log('pageOneObserver gets: ' + x);
    },
    complete: () => {
      console.log('pageOneObserver: complete');
    }
  });
}, 1000);

setTimeout(() => {
  console.log('pageTwoObserver will subscribe');
  pageTwoObserver = subject.subscribe({
    next: (x) => {
      console.log('pageTwoObserver gets: ' + x);
    },
    complete: () => {
      console.log('pageTwoObserver: complete');
    }
  });
}, 4000);

setTimeout(() => {
  console.log('pageOneObserver will unsubscribe');
  pageOneObserver.unsubscribe();
}, 7000);

setTimeout(() => {
  console.log('pageTwoObserver will unsubscribe');
  pageTwoObserver.unsubscribe();
}, 10000);

setTimeout(() => {
  console.log('pageThreeObserver will subscribe');
  pageThreeObserver = subject.subscribe({
    next: (x) => {
      console.log('pageThreeObserver gets: ' + x);
    },
    complete: () => {
      console.log('pageThreeObserver: complete');
    }
  });
}, 13000);

setTimeout(() => {
  console.log('pageThreeObserver will unsubscribe');
  pageThreeObserver.unsubscribe();
}, 16000);_
_<script src="https://unpkg.com/[email protected]/bundles/Rx.min.js"></script>_

件名を作成する簡単な方法がいくつかあります。例えば:

_sourceObservable.share();
// is the same as
sourceObservable.publish().refCount();
_
_sourceObservable.publish().refCount();
// is the same as
sourceObservable.multicast(new Rx.Subject()).refCount();
_
_sourceObservable.publishReplay().refCount();
// is the same as
sourceObservable.multicast(new Rx.ReplaySubject(1)).refCount();
_
_sourceObservable.publishBehavior().refCount();
// is the same as
sourceObservable.multicast(new Rx.BehaviorSubject(0)).refCount();
_
_sourceObservable.publishLast().refCount();
// is the same as
sourceObservable.multicast(new Rx.AsyncSubject()).refCount();
_

sourceObservable.share();にはサブジェクトファクトリも組み込まれています。つまり、ある時点でソースオブザーバブルが完了したら、sourceObservableの新しいインスタンスを作成する必要がありますが、これは、選択したサブジェクトの新しいインスタンスでのみ実行できます。 。以下の他の利用可能なサブジェクトを使用して、ファクトリ関数をマルチキャスト演算子に明示的に返す必要があります。

Rx.Subject()以外の他のサブジェクトタイプを使用し、監視可能なサブスクリプションを本当に再利用可能にしたい場合は、サブジェクトファクトリ(使用したいサブジェクトの新しいインスタンスを返す関数)を使用する必要があります。これを以下に示します。

_const {
  Observable
} = Rx;

let sourceObservable = Observable
  .create((observer) => {
    let count = 0;
    let interval = setInterval(() => {
      observer.next(count++)
    }, 700);

    setTimeout(() => {
      clearInterval(interval);
      observer.complete();
    }, 5500);

    return () => {
      clearInterval(interval);
      console.log('######## Source observable unsubscribed');
    }
  })
  .do((x) => console.log('#### Source emits: ' + x));

/* You could return whatever subject instance you like here */
let subjectFactory = () => new Rx.ReplaySubject(1);

let subject = sourceObservable
        .multicast(subjectFactory)
        .refCount();
        //.do((x) => console.log('#### Subject emits: ' + x))
        ;

let pageOneObserver;
let pageTwoObserver;
let pageThreeObserver;

setTimeout(() => {
  console.log('pageOneObserver will subscribe');
  pageOneObserver = subject.subscribe({
    next: (x) => {
      console.log('pageOneObserver gets: ' + x);
    },
    complete: () => {
      console.log('pageOneObserver: complete');
    }
  });
}, 1000);

setTimeout(() => {
  console.log('pageTwoObserver will subscribe');
  pageTwoObserver = subject.subscribe({
    next: (x) => {
      console.log('pageTwoObserver gets: ' + x);
    },
    complete: () => {
      console.log('pageTwoObserver: complete');
    }
  });
}, 4000);

setTimeout(() => {
  console.log('pageOneObserver will unsubscribe');
  pageOneObserver.unsubscribe();
}, 7000);

setTimeout(() => {
  console.log('pageTwoObserver will unsubscribe');
  pageTwoObserver.unsubscribe();
}, 10000);

setTimeout(() => {
  console.log('pageThreeObserver will subscribe');
  pageThreeObserver = subject.subscribe({
    next: (x) => {
      console.log('pageThreeObserver gets: ' + x);
    },
    complete: () => {
      console.log('pageThreeObserver: complete');
    }
  });
}, 13000);

setTimeout(() => {
  console.log('pageThreeObserver will unsubscribe');
  pageThreeObserver.unsubscribe();
}, 16000);_
_<script src="https://unpkg.com/[email protected]/bundles/Rx.min.js"></script>_

それでも不明な点があれば、遠慮なく質問してください。

2
Robert

次のユーティリティ関数が役立つ場合があります...

function subscriberCount<T>(sourceObservable: Observable<T>, description: string) {
  let counter = 0;
  return Observable.create((subscriber: Subscriber<T>) => {
    const subscription = sourceObservable.subscribe(subscriber);
    counter++;
    console.log(`${description} subscriptions: ${counter}`);

    return () => {
      subscription.unsubscribe();
      counter--;
      console.log(`${description} subscriptions: ${counter}`);
    }
  });
}

次のように使用します。

const timer$ = subscriberCount(Observable.timer(1000), 'Timer');

そして、加入者数が変わるたびにコンソールはログに記録します

1
Miller