RxJs:Observable 是有状态的吗?

RxJs: Is Observable stateful?

我问这个是因为我尝试了以下代码:

//RxJs v5.5.2; NodeJs v7.10.1
var Rx = require('rxjs/Rx'); 
var observable = Rx.Observable.of(1,2,3);

var sub = observable.subscribe(console.log);
var sub1 = observable.subscribe(console.log);

它输出 1 2 3 1 2 3(省略新行)。

但是,正如RxJs的文档所说,Observable是一个stream,为什么第二个订阅就拿到了所有的值呢?我的理解是,在第一次订阅之后,observable 已经完成并且作为一个流,它不应该产生它已经发出的值。

我是不是误会了什么?

根据上面引用的文章,Hot vs Cold Observables

Observables are just functions! Observables are functions that tie an observer to a producer. That’s it. They don’t necessarily set up the producer, they just set up an observer to listen to the producer, and generally return a teardown mechanism to remove that listener. The act of subscription is the act of “calling” the observable like a function, and passing it an observer.

因此,observable 只是生产者和观察者之间的管道,生产者可以(通常)拥有状态 'baked in'。

试图证明在你的例子中某处有一个生产者 Rx.Observable.of(1,2,3) 有点困难。

Observable.of 的来源是

export const of = ArrayObservable.of;

和数组Observable.of

static of<T>(...array: Array<T | IScheduler>): Observable<T> {

所以在这种情况下传播运算符是 生产者array 是传递给 Observable 的状态。

我找不到扩展运算符的源代码,但是这个参考 es6-equivalents-in-es5#spread-operator 给出了以下 es5 等价物

var _toArray = function (arr) {
  return Array.isArray(arr) ? arr : [].slice.call(arr);
};

function add(a, b) {
  return a + b;
}

var nums = [5, 4];
console.log(add.apply(null, _toArray(nums)));

假设上面的 _toArray() 是价差的合理近似值,我们可以说价差本质上是生产者函数。

那么,Rx.Observable.of(1,2,3)有状态吗?我们可以说是的,因为该语句包含状态,但等效于

const nums = [1,2,3]
const src = Rx.Observable.of(...nums)

我们可以看到状态实际上是可观察对象的外部。


热的呢?

一个热的可观察对象有一个生产者,它没有按需复制它的值,例如事件。

因此,当生产者很热时,可观察对象不会 'remember' 在管道设置之前状态(即发生订阅时),因此 生产者 的状态不是可观察的。

console.clear()

const eventEmitter = new EventEmitter();
var source = Rx.Observable.fromEvent(eventEmitter, 'data')

eventEmitter.emit('data', 1);
eventEmitter.emit('data', 2);
source.subscribe(x => console.log('subscription', x))
eventEmitter.emit('data', 3);
eventEmitter.emit('data', 4);
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.2/Rx.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/EventEmitter/5.2.4/EventEmitter.js"></script>