読者です 読者をやめる 読者になる 読者になる

Carpe Diem

備忘録。https://github.com/jun06t

Cold Observable と Hot Observable

概要

Observableを学ぶ上でCold ObservableとHot Observableの違いを理解していた方が良いと聞いたので調べてみました。

環境

  • rxjs 5.1.0

ざっくり表

Cold Observable Hot Observable
イメージ オンデマンド再生のような、選んだら再生。
再生するシーンは開始タイミングに依るので、人によってそれぞれ
テレビのような垂れ流し。
再生するシーンは皆同じ。
データの開始タイミング subscribe()されたら connect()されたら
データを流すStreamの数 subscribeしてる数 1つ。いくつsubscribeしても同じStreamを見る
データを受け取るStreamの数 subscribeしてる数 subscribeしてる数。
送信してるStreamは1つでも、unsubscribeは受信してる個々で行う必要がある

データの開始タイミング

Cold Observable

先程のsubscribeされたらデータ送信が開始するを確かめるために以下のコードを作ってみました。

const obs = Rx.Observable.create(
  observer => {
    const now = Date.now();
    observer.next(now);
  }
);

obs.subscribe(
  v => {
    console.log("1st: " + v);
  }
);
obs.subscribe(
  v => {
    console.log("2nd: " + v);
  }
);

Cold Sample1

結果

"1st: 1488076511239"
"2nd: 1488076511240"

Cold Observableはsubscribeする度に別のStreamとして扱われるので、subscribeが複数ある=それぞれでデータ送信を開始し、時間もズレていることがわかります。

Hot Observable

こちらはconnect()されたらデータ送信が開始するので確かめます。

const obs = Rx.Observable.create(
  observer => {
    const now = Date.now();
    observer.next(now);
  }
).publish();  // publish すればHot Observableになる

obs.subscribe(
  v => {
    console.log("1st: " + v);
  }
);
obs.subscribe(
  v => {
    console.log("2nd: " + v);
  }
);

obs.connect();

Hot Sample1

ポイントはsubscribeのあとでconnectを実行しています。
これはconnectがデータの開始タイミングなので、subscribeの前にデータを流してしまうと受け取れなくなるからです。

結果

"1st: 1488077002839"
"2nd: 1488077002839"

ちゃんと受け取れてますね。またHot Observableは同じStreamを見ているので、日付も同じ値が返ってきます。

connectを先に書いたら?

const obs = Rx.Observable.create(
  observer => {
    const now = Date.now();
    observer.next(now);
  }
).publish();

obs.connect();  // 先に書く

obs.subscribe(
  v => {
    console.log("1st: " + v);
  }
);
obs.subscribe(
  v => {
    console.log("2nd: " + v);
  }
);

Hot Sample2

結果




先程も説明したように、connect後=データの送信後にsubscribeすることになるので、返ってこなくなります。

Hot ObservableのrefCount

先程の話でHot Observableはconnectを実行して初めてデータ送信が開始されることが分かりました。
ただこれだとそのStreamがいつ開始されているのかを把握していなくてはいけません
そういった依存を解決できるのがrefCountです。

const obs = Rx.Observable
  .interval(1000)
  .take(5)
  .publish()
  .refCount();

setTimeout(
  () => {
    obs.subscribe(
      v => {
        console.log("1st: " + v);
      }
    );
  }, 1000);

setTimeout(
  () => {
    obs.subscribe(
      v => {
        console.log("2st: " + v);
      }
    );
  }, 3100);

intervalでStreamにデータを送り、1秒後と3秒後にsubscribeを開始しています。
refCountを使うとCold Observableのように、最初のsubscribeでデータ送信を開始してくれます。

Hot RefCount

結果

"1st: 0"
"1st: 1"
"1st: 2"
"2st: 2"
"1st: 3"
"2st: 3"
"1st: 4"
"2st: 4"

また結果からわかるように、Cold Observableと異なり、各subscribeが見ているStreamは1つであるというHot Observableの性質はちゃんと保持しています。

ちなみに.publish().refCount()はよく使われるので、.share()でまとめて1つで書けたりします。

例えばどこで使われているか

Cold Observable

Angular2のHttpModuleはCold Observableです。
なのでsubscribeするまではhttpリクエストを実行しませんし、もし同じStreamに対して2回subscribeすればhttpリクエストも2回実行されます。

Hot Observable

mousemoveイベントやtickerで使われます。

まとめ

ColdかHotなのかを理解して使い分けが出来ると、シーンに応じてより適切なObservableの扱い方が出来ると思います。

ソース