[英]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
. 它试图用
Observer
和Observable
的极简主义和可组合的概念填补价值生成和异步性之间的空白。
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. 术语源自初始上下文,您可以获得
pipe
, chunk
, encoding
, flush
, Duplex
, Buffer
等。通过采用实用的方法为特定用例提供显式支持,您将失去一些组合事物的能力,因为它不是那么统一。 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. 例如,您可以使用
push
在Readable
数据流和write
在Writable
,虽然,在概念上,你正在做同样的事情:发布的值。
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 }
,则可以将Observable
与Readable
流和Observer
与Writable
流匹配。 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
. 您可能已经注意到我更改了一些名称并使用了此处介绍的更简单的
Observer
和Subscription
概念,以避免由Generator
的Observables完成的重复性过载。 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. 您可以使用
Readable
, Transform
和Writable
流实现它,但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.