web-dev-qa-db-ja.com

Node.jsストリームとオブザーバブル

Observables について学んだ後、それらは Node.jsストリーム に非常によく似ています。どちらにも、新しいデータが到着したとき、エラーが発生したとき、またはデータがなくなったとき(EOF)にコンシューマに通知するメカニズムがあります。

2つの概念/機能の違いについて学びたいです。ありがとう!

69
urish

Observablesとnode.jsのStreamsの両方で同じ基礎を解決できます問題:値のシーケンスを非同期的に処理します。この2つの主な違いは、その出現の動機付けとなったコンテキストに関連していると思います。そのコンテキストは、用語とAPIに反映されています。

Observables側には、リアクティブプログラミングモデルを導入するEcmaScriptの拡張機能があります。 ObserverObservableの最小限の構成可能な概念で、値の生成と非同期性のギャップを埋めようとします。

Node.jsおよびStreams側では、ネットワークストリームとローカルファイルの非同期およびパフォーマンス処理用のインターフェイスを作成する必要がありました。用語はその初期コンテキストから派生し、pipechunkencodingflushDuplexBufferなどを取得します。特定のユースケースに対して明示的なサポートを提供する実用的なアプローチでは、物事を構成する能力が失われます。たとえば、PushストリームでReadableを使用し、writeWritableを使用しますが、概念的には同じことを実行しています:値の公開。

そのため、実際には、概念を見て、オプション_{ objectMode: true }_を使用すると、ObservableReadableストリームと、ObserverWritableストリーム。 2つのモデル間にいくつかの単純なアダプターを作成することもできます。

_var Readable = require('stream').Readable;
var Writable = require('stream').Writable;
var util = require('util');

var Observable = function(subscriber) {
    this.subscribe = subscriber;
}

var Subscription = function(unsubscribe) {
    this.unsubscribe = unsubscribe;
}

Observable.fromReadable = function(readable) {
    return new Observable(function(observer) {
        function nop() {};

        var nextFn = observer.next ? observer.next.bind(observer) : nop;
        var returnFn = observer.return ? observer.return.bind(observer) : nop;
        var throwFn = observer.throw ? observer.throw.bind(observer) : nop;

        readable.on('data', nextFn);
        readable.on('end', returnFn);
        readable.on('error', throwFn);

        return new Subscription(function() {
            readable.removeListener('data', nextFn);
            readable.removeListener('end', returnFn);
            readable.removeListener('error', throwFn);
        });
    });
}

var Observer = function(handlers) {
    function nop() {};

    this.next = handlers.next || nop;
    this.return = handlers.return || nop;
    this.throw = handlers.throw || nop;
}

Observer.fromWritable = function(writable, shouldEnd, throwFn) {
    return new Observer({
        next: writable.write.bind(writable), 
        return: shouldEnd ? writable.end.bind(writable) : function() {}, 
        throw: throwFn
    });
}
_

ここで紹介したObserverおよびSubscriptionの簡単な概念を使用して、Observablesin Generator。基本的に、Subscriptionを使用すると、Observableからサブスクライブを解除できます。とにかく、上記のコードを使用すると、pipeを使用できます。

_Observable.fromReadable(process.stdin).subscribe(Observer.fromWritable(process.stdout));
_

process.stdin.pipe(process.stdout)と比較すると、他のデータシーケンスでも機能するストリームを結合、フィルタリング、変換する方法があります。 ReadableTransform、およびWritableストリームで実現できますが、APIはReadablesをチェーンして関数を適用する代わりにサブクラス化を優先します。 Observableモデルでは、たとえば、値の変換は、変換関数をストリームに適用することに対応します。 Transformの新しいサブタイプは必要ありません。

_Observable.just = function(/*... arguments*/) {
    var values = arguments;
    return new Observable(function(observer) {
        [].forEach.call(values, function(value) {
            observer.next(value);
        });
        observer.return();
        return new Subscription(function() {});
    });
};

Observable.prototype.transform = function(transformer) {
    var source = this;
    return new Observable(function(observer) {
        return source.subscribe({
            next: function(v) {
                observer.next(transformer(v));
            },
            return: observer.return.bind(observer),
            throw: observer.throw.bind(observer)
        });
    });
};

Observable.just(1, 2, 3, 4, 5).transform(JSON.stringify)
  .subscribe(Observer.fromWritable(process.stdout))
_

結論?リアクティブモデルとObservableコンセプトをどこにでも簡単に導入できます。その概念を中心にライブラリ全体を実装することは困難です。これらの小さな機能はすべて、一貫して連携する必要があります。結局のところ、 ReactiveX プロジェクトはまだ進行中です。しかし、本当にファイルコンテンツをクライアントに送信し、エンコードを処理し、それをZip圧縮する必要がある場合は、NodeJSでサポートがあり、かなりうまく機能します。

87
m4ktub