冷热观测:是否存在“热”和“冷”运算符?


68

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

总结一下:

  • 当一个冷的可观察对象有一个观察者来消耗它们时,它会发出它的值,即观察者接收到的值的顺序与订阅时间无关。所有观察者将使用相同的值序列。
  • 一个热的可观察对象发出的值与其订阅无关,即观察者收到的值是订阅时间的函数。

但是,我觉得热还是冷仍然是造成混乱的根源。所以这是我的问题:

  • 默认情况下,所有rx观测值是否都是冷的(主题除外)?

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

  • 是否有Rx运算符将冷的可观察物转换为热的可观察物(publishshare除外)?

    例如,它如何与Rx运算符一起使用withLatestFrom?让我们cold$在某处订阅一个冷的观察。会sth$.withLatestFrom(cold$,...)很热吗?

    或者,如果我sth1$.withLatestFrom(cold$,...), sth2$.withLatestFrom(cold$,...)同时订阅sth1sth2,那么我是否总是会看到相同的值sth

  • 我以为Rx.fromEvent可以创建冷的可观察物,但事实并非如此,如答案之一所述。但是,我仍然对这种行为感到困惑:https : //codepen.io/anon/pen/NqQMJR? editors =101。不同的订阅从相同的可观察值中获得不同的值。不是click事件共享?


冷观测值上的语句“所有观察者将消耗相同的值序列”是不正确的。很多时候的确如此,但即使是我将数组变为一个可观察变量的简单情况,其值也会改变。我同样可以创建一个可观察到的随机数生成器,该生成器很冷,但很少重复使用。

没错 实际上,链接的Codepen中就是这种情况,当时正是那个人提出了我的问题。希望我提供的答案可以更清楚地了解订阅发生了什么。
user3743222 '02

Answers:


86

几个月后,我回到了最初的问题,并希望与此同时分享所获得的知识。我将使用以下代码作为说明支持(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(){});

如答案之一所述,定义可观察对象将导致一系列回调和参数注册。数据流必须插入,并且可以通过subscribe函数来完成。此后可以找到(为说明简化)的详细流程。

简化流程图

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

该观察者依次向下一个观察者发出,导致下游数据流向下到接收器观察者。以下简化图示显示了两个订户订阅同一可观察项时的订户和数据流。

冷观测简化流程图

可以通过使用主题或通过multicast运算符(及其派生形式,请参见下面的注释3)来创建热的可观察物。

multicast引擎盖下运营商利用一个主题,并返回一个可连接的可观察的。对操作员的所有订阅都将是对内部主题的订阅。当connect被调用时,内部主体所预订的上游观察到的,和数据流下游。主体在内部操作订阅的观察者列表,并将传入的数据多播到所有订阅的观察者。

下图总结了这种情况。

热观测简化流程图

最后,更重要的是要了解由观察者模式和运算符的实现引起的数据流。

例如,如果obs很热,是hotOrCold = obs.op1冷还是热?无论答案是:

  • 如果没有订阅者obs.op1,则不会有数据流过op1。如果有热点用户obs,那obs.op1将可能丢失数据
  • 假设它op1不是一个类似于多播的运算符,则两次hotOrCold订阅将会订阅两次op1,并且来自的每个值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主题的​​语义是什么?


很好的学习。感谢您分享这个!您能否添加解释“发布运营商”和“共享运营商”的含义?
伊万·克莱斯宁

1
publish运营商是multicast有不同类型的作为参数传递科目运营商。对于publish例如,在这里看到:github.com/Reactive-Extensions/RxJS/blob/master/src/core/linq/...share运营商通过附加得到.refCount()publish运算符。对于share例如,参照github.com/Reactive-Extensions/RxJS/blob/master/src/core/linq/...
user3743222'1

2
正确地记录主题仍然是一件好事(当前的文件可能很难弄清楚)。我认为最好的方法是,如果您记录有关该主题的特定问题(无双关语)。在那里回答这个问题将超出范围,这将允许添加更多详细信息。目前的答案已经很长了,我想一直专注于这个问题。问题可以是what are the semantics of the miscellaneous kinds of Rxjs subjects简洁的表达方式,也可以是您发现的任何方式。
user3743222'1

你是对的。我已经发布了一个问题:stackoverflow.com/questions/34849873/…
伊万·克莱斯宁

还有一个关于“ onComplete”的问题,请参见stackoverflow.com/questions/34850043/…。也许您已经为自己做了详细说明。操作员标题现在很清楚,谢谢。
伊万·克莱辛

10

您的总结以及所链接的问题都是正确的,我认为该术语可能会使您感到困惑。我建议您将热和冷可观察物分别视为主动和被动可观察物。

也就是说,一个活跃的(热的)可观察对象将发出项目,无论有人是否订阅。同样,在规范示例中,无论是否有人在听,按钮单击事件都会发生。这种区别很重要,因为例如,如果我单击一个按钮,然后订阅按钮单击(按该顺序),则不会看到已经发生的按钮单击。

被动(冷)可观察对象将等到订户存在后再发射物品。想象一下一个按钮,除非有人正在听事件,否则您无法单击它-这样可以确保您始终看到每个单击事件。

默认情况下,所有Rx观测值是否都是“冷”的(或被动的)?不可以,Rx.fromEvent(input, 'click')例如,可以观察到热点(或活跃)。

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

事实并非如此。

是否有Rx运算符将冷观测值变成热观测值?

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

您命名的两个运算符(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$,...)订阅并订阅foobar,那么我始终会看到相同的值吗?

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

我以为Rx.fromEvent可以创建冷的可观察物,但事实并非如此,如答案之一所述。但是,我仍然对此行为感到困惑:codepen.io/anon/pen/NqQMJR?editors=101。不同的订阅从相同的可观察值中获得不同的值。不是click事件共享?

我将通过谜题为您指出一个很好的答案,以回答我对相同行为的一个问题。该答案将比我能更好地解释它,但是要点是源(单击事件)是“共享的”,是的,但是您对此没有进行操作。如果您不仅要共享单击事件,而且还希望共享它的操作,则需要明确共享。


Or if I do foo$.withLatestFrom(cold$,...), bar$.withLatestFrom(cold$,...) and subscribe to foo and bar, will I always see the same values for both?我的问题是cold$,两种情况下获取的价值是否相同。我的问题是的语义withLatestFrom。我知道我们对Cold $有两个不同的订阅,因此我希望可以根据订阅时间提取两个不同的值,这会破坏操作符的语义,因此我希望操作符cold$变成热流。但是最好是进行测试,我会尽快发布答案。
user3743222's

4

values在您的Codepen中是懒惰的-在有订阅之前什么也不会发生,这时它会贯穿并连接起来。因此,在您的示例中,尽管您订阅了相同的变量,但它创建了两个不同的流;每个订阅呼叫一个。

你可以认为values作为流的生成clickmap附着。

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


3

这不是您所有问题的答案(我想知道所有问题!),但是可以肯定的是,所有fromEventObservable都很热门。单击似乎不是因为它不是像mousemove那样的“连续”事件,但是创建Observable时,无论如何,对源(addEventListeneron调用)的订阅仅执行一次。好热 您可以在这里那里的操作员的源代码中看到它-创建的可观察对象是shared,无论事件名称或源是什么。


但是,如果您将该点击映射到一个函数,并对该函数进行两次订阅,则对于同一点击,它将两次通过可观察的链。Codepen在这里:codepen.io/anon/pen/NqQMJR?editors=101
user3743222

Erik,为所有这些问题添加了答案。如果您有时间,请告诉我您的想法。
user3743222
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.