71

我回顾了以下 SO 问题: 什么是冷热观测值?

总结一下:

  • 一个冷的可观察对象在它有一个观察者来消费它们时发出它的值,即观察者接收到的值的序列与订阅时间无关。所有观察者都将使用相同的值序列。
  • hot observable 独立于其订阅发出值,即观察者接收到的值是订阅时间的函数。

然而,我觉得热与冷仍然是混乱的根源。所以这是我的问题:

  • 默认情况下所有 rx 可观察对象都是冷的(主题除外)吗?

    我经常读到事件是热可观察对象的典型隐喻,但我也读到这Rx.fromEvent(input, 'click')是冷可观察对象(?)。

  • 是否有/什么是 Rx 操作符将冷的 observables 变成热的 observables(除了publish, 和share)?

    例如,它如何与 Rx 运算符一起使用withLatestFrom?让我们cold$成为一个冷的 observable,它已经在某个地方被订阅了。会sth$.withLatestFrom(cold$,...)成为热点观察者吗?

    或者如果我sth1$.withLatestFrom(cold$,...), sth2$.withLatestFrom(cold$,...)订阅并订阅sth1and sth2,我会看到两者的值相同sth吗?

  • 我认为Rx.fromEvent会产生冷的可观察量,但事实并非如此,正如其中一个答案中提到的那样。但是,我仍然对这种行为感到困惑:https ://codepen.io/anon/pen/NqQMJR?editors=101 。不同的订阅从同一个 observable 获得不同的值。活动不是click共享的吗?

4

4 回答 4

91

几个月后我回来回答我原来的问题,同时想分享所获得的知识。我将使用以下代码作为解释支持(jsfiddle):

var ta_count = document.getElementById('ta_count');
var ta_result = document.getElementById('ta_result');
var threshold = 3;

function emits ( who, who_ ) {return function ( x ) {
  who.innerHTML = [who.innerHTML, who_ + " emits " + JSON.stringify(x)].join("\n");
};}

var messages$ = Rx.Observable.create(function (observer){
  var count= 0;
  setInterval(function(){
    observer.onNext(++count);
  }, 1000)
})
.do(emits(ta_count, 'count'))
.map(function(count){return count < threshold})
.do(emits(ta_result, 'result'))

messages$.subscribe(function(){});

正如一个答案中提到的,定义一个 observable 会导致一系列回调和参数注册。必须启动数据流,这是通过subscribe函数完成的。之后可以找到一个(为说明而简化)详细流程。

简化流程图

Observables 默认是冷的。订阅 observable 将导致上游订阅链发生。最后一个订阅导致执行一个函数,该函数将处理一个源并将其数据发送给它的观察者。

该观察者轮流向下一个观察者发出数据,从而产生下游数据流,直至接收器观察者。下面的简化图显示了两个订阅者订阅同一个 observable 时的订阅和数据流。

冷观测简化流程图

Hot observables 可以通过使用主题或通过multicast操作符(及其派生词,请参见下面的注释 3)来创建。

引擎盖下的multicast操作员使用主题并返回可连接的可观察对象。对运算符的所有订阅都将是对内部主题的订阅。当connect被调用时,内部主体订阅上游的 observable,数据流向下游。主体在内部操作订阅的观察者列表并将传入数据多播给所有订阅的观察者。

下图概括了这种情况。

热可观测简化流程图

最后,更重要的是理解观察者模式引起的数据流和操作符的实现。

例如,如果obs是热的,是hotOrCold = obs.op1冷还是热?不管答案是什么:

  • 如果没有订阅者obs.op1,则不会有数据流过op1。如果有hot的订阅者obs,那意味着obs.op1可能会丢失一些数据。
  • 假设这op1不是一个类似多播的运算符,订阅两次 tohotOrCold将订阅两次op1,并且 from 的每个值obs将流过两次op1

备注:

  1. 此信息应该对 Rxjs v4 有效。虽然第 5 版经历了相当大的变化,但其中大部分仍然逐字适用。
  2. 未表示取消订阅、错误和完成流程,因为它们不在问题的范围内。调度程序也不被考虑在内。除其他外,它们会影响数据流的时间,但不会影响数据流的方向和内容。
  3. 根据用于组播的主体类型,有不同的派生组播算子:

Subject type | `Publish` Operator | `Share` operator ------------------ | --------------------------- | ----------------- Rx.Subject | Rx.Observable.publish | share Rx.BehaviorSubject | Rx.Observable.publishValue | shareValue Rx.AsyncSubject | Rx.Observable.publishLast | N/A Rx.ReplaySubject | Rx.Observable.replay | shareReplay

更新:另请参阅Ben Lesh 关于该主题的以下文章(此处和此处)。

有关主题的更多详细信息可以在另一个 SO 问题中找到:不同 RxJS 主题的语义是什么?

于 2016-01-08T04:20:08.820 回答
10

您的摘要和链接的问题都是正确的,我认为这些术语可能会让您感到困惑。我建议您将冷热可观察对象视为主动和被动可观察对象(分别)。

也就是说,无论是否有人订阅,一个活跃的(热的)observable 都会发射项目。再一次,典型的例子是,无论是否有人在听,按钮点击事件都会发生。这种区别很重要,因为例如,如果我单击一个按钮,然后订阅按钮单击(按此顺序),我将看不到已经发生的按钮单击。

被动(冷)可观察对象将等到订阅者存在后再发送项目。想象一个按钮,在有人监听事件之前你不能点击它——这将确保你总是看到每一个点击事件。

默认情况下,所有 Rx 可观察对象都是“冷的”(或被动的)吗?不,Rx.fromEvent(input, 'click')例如是一个热的(或活跃的)可观察的。

我还读到这Rx.fromEvent(input, 'click')是一个冷的可观察的(?)

事实并非如此。

是否有 Rx 操作符可以将冷的 observable 变成热的 observable?

将热(主动)可观察对象转变为冷(被动)可观察对象的概念是这样的:您需要记录在没有订阅任何内容时发生的事件,并将这些项目(以各种方式)提供给未来出现的订阅者。一种方法是使用Subject。例如,您可以使用 aReplaySubject来缓冲发出的项目并将它们重播给未来的订阅者。

您命名的两个运算符 (publishshare) 都在内部使用主题来提供该功能。

它如何与 Rx 运算符一起使用withLatestFrom?让我们cold$成为一个已订阅的冷可观察对象。会something$.withLatestFrom(cold$,...)成为热点观察者吗?

如果something是一个热可观察的,那么是的。如果something是冷观测,那么不是。回到事件示例,如果something是按钮单击事件流:

var clickWith3 = Rx.fromEvent(input, 'click')
    .withLatest(Rx.Observable.from([1, 2, 3]);

或者,如果我foo$.withLatestFrom(cold$,...), bar$.withLatestFrom(cold$,...)订阅并订阅fooand bar,我是否总是会看到两者的相同值?

不总是。同样,例如,如果foobar是点击不同的按钮,那么您会看到不同的值。同样,即使它们是同一个按钮,如果您的组合函数( 的第二个参数withLatest)对于相同的输入没有返回相同的结果,那么您将不会看到相同的值(因为它会被调用两次,如解释以下)。

我认为Rx.fromEvent会产生冷的可观察量,但事实并非如此,正如其中一个答案中提到的那样。但是,我仍然对这种行为感到困惑:codepen.io/anon/pen/NqQMJR ?editors=101 。不同的订阅从同一个 observable 获得不同的值。活动不是click共享的吗?

我将向您指出Enigmativity对我对相同行为提出的问题的这个很好的回答。该答案将比我更好地解释它,但其要点是源(单击事件)是“共享的”,是的,但您对它的操作不是。如果您不仅要共享单击事件,还要共享对它的操作,则需要明确地这样做。

于 2015-09-06T22:27:50.760 回答
4

values在您的代码笔中是懒惰的-在订阅某些内容之前什么都不会发生,此时它会运行并连接起来。因此,在您的示例中,尽管您订阅了同一个变量,但它正在创建两个不同的流;每个订阅调用一个。

您可以将其values视为附加的流的生成clickmap

.share()在该地图的末尾会创建我们期望的行为,因为它是隐式订阅的。

于 2015-09-03T02:08:22.653 回答
3

这不是你所有问题的答案(我想知道所有问题!)但可以肯定的是,所有fromEvent的 Observables 都是热门的。Click 似乎不是因为它不是像 mousemove 这样的“连续”事件,但无论如何,在创建 Observable 时,对源(addEventListeneron调用)的订阅只完成一次。所以很热。您可以在这里那里在操作员的源代码中看到它-share无论事件名称或来源是什么,创建的 observable 都是d。

于 2015-08-25T10:25:07.020 回答