web-dev-qa-db-ja.com

RxJs 5 share()演算子はどのように機能しますか?

RxJs 5 share()演算子がどのように機能するかは100%明確ではありません。ここで 最新のドキュメント を参照してください。質問のJsbin ここ

一連の0〜2のオブザーバブルを作成すると、各値は1秒ごとに区切られます。

_var source = Rx.Observable.interval(1000)
.take(5)
.do(function (x) {
    console.log('some side effect');
});
_

そして、このオブザーバブルの2つのサブスクライバーを作成した場合:

_source.subscribe((n) => console.log("subscriptor 1 = " + n));
source.subscribe((n) => console.log("subscriptor 2 = " + n));
_

コンソールでこれを取得します:

_"some side effect ..."
"subscriptor 1 = 0"
"some side effect ..."
"subscriptor 2 = 0"
"some side effect ..."
"subscriptor 1 = 1"
"some side effect ..."
"subscriptor 2 = 1"
"some side effect ..."
"subscriptor 1 = 2"
"some side effect ..."
"subscriptor 2 = 2"
_

各サブスクリプションは同じObservableにサブスクライブすると思っていましたが、そうではないようです!サブスクライブする行為のように、完全に独立したObservableが作成されます!

しかし、share()演算子がソースobservableに追加された場合:

_var source = Rx.Observable.interval(1000)
.take(3)
.do(function (x) {
    console.log('some side effect ...');
})
.share();
_

次に、これを取得します。

_"some side effect ..."
"subscriptor 1 = 0"
"subscriptor 2 = 0"
"some side effect ..."
"subscriptor 1 = 1"
"subscriptor 2 = 1"
"some side effect ..."
"subscriptor 1 = 2"
"subscriptor 2 = 2"
_

これは、share()なしで期待するものです。

ここで何が起こっていますか、share() operatorはどのように機能しますか?各サブスクリプションは、新しいオブザーバブルチェーンを作成しますか?

28

ドキュメントリンクがRxJS v4のように見える一方で、RxJS v5を使用していることに注意してください。具体的なことは覚えていませんが、share演算子は、特に完了と再購読に関してはいくつかの変更を経たと思いますが、私のWordを受け入れません。

質問に戻って、あなたの研究で示したように、あなたの期待は図書館の設計に対応していません。オブザーバブルは、データフローを遅延的にインスタンス化し、サブスクライバーがサブスクライブすると、具体的にデータフローを開始します。 2番目のサブスクライバーが同じオブザーバブルをサブスクライブすると、別の新しいデータフローが最初のサブスクライバーであるかのように開始されます(したがって、各サブスクリプションは、前述のようにオブザーバブルの新しいチェーンを作成します)。これは、RxJSの用語ではコールドオブザーバブルと呼ばれるものであり、RxJSオブザーバブルのデフォルトの動作です。データが到着した時点で持っているサブスクライバにデータを送信するオブザーバブルが必要な場合、これはホットオブザーバブルと呼ばれます。ホットオブザーバブルを取得する1つの方法は、share演算子を使用することです。

ここで、図解されたサブスクリプションとデータフローを見つけることができます: ホットおよびコールドオブザーバブル:「ホット」および「コールド」演算子はありますか? (これはRxJS v4で有効ですが、ほとんどはv5で有効です) 。

19
user3743222

次の2つの条件が満たされた場合、共有は観察可能な「ホット」になります。

  1. サブスクライバーの数> 0
  2. そして、オブザーバブルは完了していません

シナリオ1:サブスクライバーの数が0を超えており、新しいサブスクリプションの前にオブザーバブルが完了していない

var shared  = rx.Observable.interval(5000).take(2).share();
var startTime = Date.now();
var log = (x) => (value) => { 
    console.log(`onNext for ${x}, Delay: ${Date.now() - startTime} , Value: ${value}`);
};

var observer1 = shared.subscribe(log('observer1')),
    observer2;

setTimeout(()=>{
    observer2 = shared.subscribe(log('observer2'));
}, 3000);

// emission for both observer 1 and observer 2, with the samve value at startTime + 5 seconds
// another emission for both observers at: startTime + 10 seconds

シナリオ2:新しいサブスクリプションの前のサブスクライバーの数はゼロです。 「冷たくなる」

 var shared  = rx.Observable.interval(5000).take(2).share();
    var startTime = Date.now();
    var log = (x) => (value) => { 
    console.log(`onNext for ${x}, Delay: ${Date.now() - startTime} , Value: ${value}`);
};

var observer1 = shared.subscribe(log('observer1')),
    observer2;

setTimeout(()=>{
    observer1.unsubscribe(); 
}, 1000);

setTimeout(()=>{
    observer2 = shared.subscribe(log('observer2')); // number of subscribers is 0 at this time
}, 3000);
// observer2's onNext is called at startTime + 8 seconds
// observer2's onNext is called at startTime + 13 seconds

シナリオ3:新しいサブスクリプションの前にオブザーバブルが完了したとき。 「冷たくなる」

 var shared  = rx.Observable.interval(5000).take(2).share();
    var startTime = Date.now();
    var log = (x) => (value) => { 
        console.log(`onNext for ${x}, Delay: ${Date.now() - startTime} , Value: ${value}`);
    };

var observer1 = shared.subscribe(log('observer1')),
    observer2;

setTimeout(()=>{
    observer2 = shared.subscribe(log('observer2'));
}, 12000);

// 2 emission for observable 1, at startTime + 5 secs, and at startTime + 10secs
// 2 emissions for observable 2,at startTime + 12 + 5 secs, and at startTime + 12 + 10secs
13
sbr