简体   繁体   English

Node.js Streams vs. Observables

[英]Node.js Streams vs. Observables

After learning about Observables , I find them quite similar to Node.js streams . 在了解了Observables之后 ,我发现它们与Node.js流非常相似。 Both have a mechanism of notifying the consumer whenever new data arrives, an error occurs or there is no more data (EOF). 两者都有一种机制,可以在新数据到达时通知消费者,发生错误或没有更多数据(EOF)。

I would love to learn about the conceptual/functional differences between the two. 我很想了解两者之间的概念/功能差异。 Thanks! 谢谢!

Both Observables and node.js's Streams allow you to solve the same underlying problem: asynchronously process a sequence of values. Observables和node.js的Streams都允许您解决相同的底层问题:异步处理一系列值。 The main difference between the two, I believe, is related to the context that motivated its appearance. 我相信,两者之间的主要区别在于它的外观背景。 That context is reflected in the terminology and API. 该上下文反映在术语和API中。

On the Observables side you have an extension to EcmaScript that introduces the reactive programming model. Observables方面,您有一个EcmaScript的扩展,它引入了反应式编程模型。 It tries to fill the gap between value generation and asynchronicity with the minimalist and composable concepts of Observer and Observable . 它试图用ObserverObservable的极简主义和可组合的概念填补价值生成和异步性之间的空白。

On node.js and Streams side you wanted to create an interface for the asynchronous and performant processing of network streams and local files. 在node.js和Streams端,您希望为网络流和本地文件的异步和高性能处理创建一个接口。 The terminology derives from that initial context and you get pipe , chunk , encoding , flush , Duplex , Buffer , etc. By having a pragmatic approach that provides explicit support for particular use cases you lose some ability to compose things because it's not as uniform. 术语源自初始上下文,您可以获得pipechunkencodingflushDuplexBuffer等。通过采用实用的方法为特定用例提供显式支持,您将失去一些组合事物的能力,因为它不是那么统一。 For example, you use push on a Readable stream and write on a Writable although, conceptually, you are doing the same thing: publishing a value. 例如,您可以使用pushReadable数据流和writeWritable ,虽然,在概念上,你正在做同样的事情:发布的值。

So, in practice, if you look at the concepts, and if you use the option { objectMode: true } , you can match Observable with the Readable stream and Observer with the Writable stream. 因此,在实践中,如果查看概念,并且使用选项{ objectMode: true } ,则可以将ObservableReadable流和ObserverWritable流匹配。 You can even create some simple adapters between the two models. 您甚至可以在两个模型之间创建一些简单的适配器。

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
    });
}

You may have noticed that I changed a few names and used the simpler concepts of Observer and Subscription , introduced here, to avoid the overload of reponsibilities done by Observables in Generator . 您可能已经注意到我更改了一些名称并使用了此处介绍的更简单的ObserverSubscription概念,以避免由GeneratorObservables完成的重复性过载。 Basically, the Subscription allows you to unsubscribe from the Observable . 基本上, Subscription允许您取消订阅Observable Anyway, with the above code you can have a pipe . 无论如何,使用上面的代码你可以有一个pipe

Observable.fromReadable(process.stdin).subscribe(Observer.fromWritable(process.stdout));

Compared with process.stdin.pipe(process.stdout) , what you have is a way to combine, filter, and transform streams that also works for any other sequence of data. process.stdin.pipe(process.stdout)相比,您所拥有的是一种组合,过滤和转换流的方法,这些流也适用于任何其他数据序列。 You can achieve it with Readable , Transform , and Writable streams but the API favors subclassing instead of chaining Readable s and applying functions. 您可以使用ReadableTransformWritable流实现它,但API支持子类化而不是链接Readable和应用函数。 On the Observable model, For example, transforming values corresponds to applying a transformer function to the stream. Observable模型上,例如,转换值对应于将变换器函数应用于流。 It does not require a new subtype of Transform . 它不需要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))

The conclusion? 结论? It's easy to introduce the reactive model and the Observable concept anywhere. 在任何地方都可以很容易地引入反应模型和Observable概念。 It's harder to implement an entire library around that concept. 围绕这个概念实现整个库更加困难。 All those little functions need to work together consistently. 所有这些小功能需要始终如一地协同工作。 After all, the ReactiveX project is still going at it. 毕竟, ReactiveX项目仍在继续。 But if you really need to send the file content to the client, deal with encoding, and zip it then the support it's there, in NodeJS, and it works pretty well. 但是如果你真的需要将文件内容发送到客户端,处理编码,然后在NodeJS中将其压缩,那么它的效果非常好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM