最近,我开始关注反应式编程概念的RxJS和RxJava(来自Netflix)库。
Node.js基于事件循环工作,为您提供异步编程的所有工具,随后的节点库(如“cluster”)可帮助您充分利用多核机器。 Node.js还为您提供了EventEmitter功能,您可以在其中订阅事件并以异步方式对其进行操作。
另一方面,如果我理解正确RxJS(和一般的反应式编程)的工作原理是事件流,订阅事件流,异步转换事件流数据。
所以,问题是在Node.js中使用Rx包是什么意思。 Node的事件循环,事件发射器和放大器有多么不同。订阅Rx的流和订阅。
答案 0 :(得分:83)
我的回答〜两年前错了,我无法对其进行大幅编辑或删除。 Observable与EventEmitters不同。在某些情况下,可能像EventEmitters一样,即当它们使用RxJS主题进行多播时,但通常它们不像EventEmitters那样。
简而言之,RxJS Subject 就像一个EventEmitter,但RxJS Observable 是一个更通用的接口。 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')
将不会发生。另外,“调用”或“订阅”是一个独立的操作:两个函数调用触发两个单独的副作用,两个Observable订阅触发两个单独的副作用。与EventEmitters相反,EventEmitters分享副作用并且无论订阅者是否存在都有急切的执行,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和函数之间究竟有什么区别呢?
Observable可以随时间“返回”多个值,而功能则不能。你不能这样做:
function foo() {
console.log('Hello');
return 42;
return 100; // dead code. will never happen
}
函数只能返回一个值。但是,可观测量可以做到这一点:
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()
表示“立即给我一个值(同步)”obsv.subscribe()
表示“给我价值。也许很多,也许同步,也许是异步的”Observables是函数的泛化(没有参数)。
答案 1 :(得分:1)
侦听器何时附加到发射器?
使用事件发射器,只要他们感兴趣的事件发生,就会通知侦听器。在事件发生后添加新的侦听器时,他将不知道过去的事件。此外,新的听众也不会知道之前发生的事件的历史。当然,我们可以手动编程我们的发射器和监听器来处理这个自定义逻辑。
使用反应流,订阅者可以获得从头开始发生的事件流。所以他订阅的时间并不严格。现在,他可以在流上执行各种操作,以获得他感兴趣的事件子流。
这样做的好处就出来了:
高阶订单流:
高阶流是“流的流”:其事件值本身就是流的流。
使用Event发射器,一种方法是将相同的侦听器连接到多个事件发射器。当我们需要关联不同发射器上发生的事件时,它变得复杂。
使用反应流,这是轻而易举的事。来自mostjs的示例(这是一个反应式编程库,如RxJS,但性能更高)
const firstClick = most.fromEvent('click', document).take(1);
const mousemovesAfterFirstClick = firstClick.map(() =>
most.fromEvent('mousemove', document)
.takeUntil(most.of().delay(5000)))
在上面的示例中,我们将点击事件与鼠标移动事件相关联。当事件以流形式提供时,跨事件的演绎模式变得更容易实现。
话虽如此,使用EventEmitter,我们可以通过设计我们的发射器和监听器来完成所有这些。它需要过度工程,因为它不适用于这种情况。反应流可以如此流利地进行,因为它旨在解决这些问题。