最近、リアクティブプログラミングの概念で動作する RxJS およびRxJava(from Netflix)ライブラリを検討し始めました。
Node.jsはイベントループに基づいて動作し、非同期プログラミングのすべての武器を提供し、「クラスター」のような後続のノードライブラリはマルチコアマシンを最大限に活用するのに役立ちます。また、Node.jsはEventEmitter機能も提供します。この機能を使用すると、イベントをサブスクライブし、非同期でイベントを処理できます。
一方、RxJS(および一般的なリアクティブプログラミング)が正しく理解されていれば、イベントストリームの原理に基づいて動作し、イベントストリームにサブスクライブし、イベントストリームデータを非同期に変換します。
したがって、質問はNode.jsでRxパッケージを使用することの意味です。 Nodeのイベントループ、イベントエミッター、Rxのストリームとサブスクリプションのサブスクリプションはどの程度異なりますか。
オブザーバブルは、EventEmittersとは異なります。それらはmay act場合によってはEventEmittersのように、つまりRxJS Subjectを使用してマルチキャストされる場合ですが、通常はEventEmittersのように動作しません。
つまり、RxJS SubjectはEventEmitterに似ていますが、RxJS Observableはより一般的なインターフェイスです。 オブザーバブルは引数なしの関数により似ています。
以下を考慮してください。
_function foo() {
console.log('Hello');
return 42;
}
var x = foo.call(); // same as foo()
console.log(x);
var y = foo.call(); // same as foo()
console.log(y);
_
もちろん、私たちは皆、出力として見ることを期待しています:
_"Hello"
42
"Hello"
42
_
上記と同じ動作を記述できますが、Observablesを使用します。
_var foo = Rx.Observable.create(function (observer) {
console.log('Hello');
observer.next(42);
});
foo.subscribe(function (x) {
console.log(x);
});
foo.subscribe(function (y) {
console.log(y);
});
_
そして、出力は同じです:
_"Hello"
42
"Hello"
42
_
これは、関数とObservableの両方が遅延計算であるためです。関数を呼び出さないと、console.log('Hello')
は発生しません。また、Observablesでは、「_」(subscribe
)を「呼び出さない」場合、console.log('Hello')
は発生しません。さらに、「呼び出し」または「サブスクライブ」は独立した操作です。2つの関数呼び出しが2つの別個の副作用をトリガーし、2つの監視可能なサブスクライブが2つの別個の副作用をトリガーします。副作用を共有し、サブスクライバの存在に関係なく実行を積極的に行うEventEmitterとは対照的に、Observablesは実行を共有せず、遅延します。
これまでのところ、関数とObservableの動作に違いはありません。このStackOverflowの質問は、「RxJS Observables vs functions?」.
Observablesは非同期であると主張する人もいます。それは真実ではありません。次のように、関数呼び出しをログで囲む場合:
_console.log('before');
console.log(foo.call());
console.log('after');
_
明らかに出力が表示されます:
_"before"
"Hello"
42
"after"
_
そして、これはObservablesと同じ動作です:
_console.log('before');
foo.subscribe(function (x) {
console.log(x);
});
console.log('after');
_
そして出力:
_"before"
"Hello"
42
"after"
_
これは、foo
のサブスクリプションが関数のように完全に同期したことを証明しています。
それでは、Observableと関数の実際の違いは何ですか?
オブザーバブルは時間の経過とともに複数の値を「返す」ことができます、機能では不可能なことです。これはできません:
_function foo() {
console.log('Hello');
return 42;
return 100; // dead code. will never happen
}
_
関数は1つの値のみを返すことができます。ただし、オブザーバブルはこれを行うことができます。
_var foo = Rx.Observable.create(function (observer) {
console.log('Hello');
observer.next(42);
observer.next(100); // "return" another value
observer.next(200);
});
console.log('before');
foo.subscribe(function (x) {
console.log(x);
});
console.log('after');
_
同期出力の場合:
_"before"
"Hello"
42
100
200
"after"
_
ただし、値を非同期的に「返す」こともできます。
_var foo = Rx.Observable.create(function (observer) {
console.log('Hello');
observer.next(42);
observer.next(100);
observer.next(200);
setTimeout(function () {
observer.next(300);
}, 1000);
});
_
出力あり:
_"before"
"Hello"
42
100
200
"after"
300
_
結論として、
func.call()
は、「1つの値をすぐに(同期的に)与える」を意味しますobsv.subscribe()
は「値を与えてください。おそらくそれらの多くは、おそらく同期的に、おそらく非同期的に "これが、Observablesが(引数を持たない)関数の一般化である方法です。
リスナーはいつエミッターにアタッチされますか?
イベントエミッターを使用すると、イベントに関心があることがリスナーに通知されます。イベントが発生した後に新しいリスナーが追加されると、彼は過去のイベントについては知りません。また、新しいリスナーは、以前に発生したイベントの履歴を知りません。もちろん、エミッタとリスナーを手動でプログラムして、このカスタムロジックを処理することもできます。
リアクティブストリームでは、サブスクライバーは最初から発生したイベントのストリームを取得します。したがって、彼が購読する時間は厳密ではありません。これで、ストリームに対してさまざまな操作を実行して、関心のあるイベントのサブストリームを取得できます。
この利点は次のとおりです。
高次ストリーム:
高次ストリームは「ストリームのストリーム」、つまりイベント値自体がストリームであるストリームです。
イベントエミッターを使用する方法の1つは、同じリスナーを複数のイベントエミッターにアタッチすることです。異なるエミッターで発生したイベントを相関させる必要がある場合、複雑になります。
リアクティブストリームを使用すると、非常に簡単です。 mostjs の例(これは、RxJSに似ていますが、よりパフォーマンスの高いリアクティブプログラミングライブラリです)
const firstClick = most.fromEvent('click', document).take(1);
const mousemovesAfterFirstClick = firstClick.map(() =>
most.fromEvent('mousemove', document)
.takeUntil(most.of().delay(5000)))
上記の例では、クリックイベントとマウス移動イベントを関連付けています。イベントがストリームとして利用可能な場合、イベント全体のパターンを差し引くことは非常に簡単になります。
そうは言っても、EventEmitterを使用すると、エミッターとリスナーを過剰にエンジニアリングすることで、これらすべてを実現できます。このようなシナリオの最初の場所ではないため、エンジニアリングが必要です。一方、リアクティブストリームは、このような問題を解決することを目的としているため、これを流に実行します。