【问题标题】:Hot and Cold observables: are there 'hot' and 'cold' operators?Hot and Cold observables:有“热”和“冷”运算符吗?
【发布时间】:2015-11-18 08:34:37
【问题描述】:

我查看了以下 SO 问题: What are the Hot and Cold observables?

总结一下:

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

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

  • 默认情况下所有 rx observables 都是冷的吗(主题除外)?

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

  • 是否有/有哪些 Rx 操作符可以将冷的 observables 变成热的 observables(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。不同的订阅从同一个 observable 获得不同的值。 click 事件不是共享了吗?

【问题讨论】:

  • 在冷可观察对象上的“所有观察者将使用相同的值序列”的陈述是不正确的。很多时候确实如此,但即使是改变数组中的一个元素并将其转换为可观察对象的简单情况也意味着值会发生变化。我同样可以创建一个随机数生成器 observable,它会很冷,但很少重复数字。
  • 没错。实际上,这正是链接代码笔中的特色以及当时谁产生了我的问题。希望我提供的答案更清楚地说明订阅会发生什么。

标签: javascript angular rxjs reactive-programming rxjs5


【解决方案1】:

几个月后我会回来回答我原来的问题,同时想分享所获得的知识。 我将使用以下代码作为解释支持(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 时的订阅和数据流。

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

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

下图概括了这种情况。

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

例如,如果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 关于该主题的 the following articles, here, and there)。

可以在另一个 SO 问题中找到有关主题的更多详细信息:What are the semantics of different RxJS subjects?

【讨论】:

  • 伟大的研究。感谢您分享这个!您能否解释一下“发布运营商”和“共享运营商”的含义?
  • 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/…
  • 正确记录主题仍然很好(当前的文档可能很难弄清楚)。我认为最好的方法是如果您记录有关该主题的特定问题(没有双关语)。在那里回答这个问题超出了范围,它允许添加更多细节。目前的答案已经很长了,我想把它集中在这个问题上。问题可以是what are the semantics of the miscellaneous kinds of Rxjs subjects 或任何您能找到的简洁表述方式。
  • 你是对的。我已经发布了一个问题:stackoverflow.com/questions/34849873/….
  • 还有一个关于“onComplete”的问题stackoverflow.com/questions/34850043/…。也许您也已经为自己详细说明了这一点。操作员的头衔现在很清楚了,谢谢。
【解决方案2】:

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

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

被动(冷)observable 将等到订阅者存在后再发出项目。想象一个按钮,在有人监听事件之前您无法点击它——这将确保您始终看到每个点击事件。

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

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

事实并非如此。

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

将热(主动)可观察对象转变为冷(被动)可观察对象的概念是这样的:您需要记录在没有订阅任何内容时发生的事件,并将这些项目(以各种方式)提供给进来的订阅者未来。一种方法是使用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。不同的订阅从同一个 observable 获得不同的值。 click 事件不是共享的吗?

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

【讨论】:

  • 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$变成一个热流。但最好是测试,我会尽快发布答案。
【解决方案3】:

values 在你的 codepen 中是懒惰的 - 直到有东西订阅,然后它才会运行并连接起来。因此,在您的示例中,尽管您订阅了同一个变量,但它正在创建两个不同的流;每个订阅调用一个。

您可以将values 视为click 的流生成器​​,并附加了map

该地图末尾的.share() 会产生我们预期的行为,因为它是隐式订阅。

【讨论】:

    【解决方案4】:

    这不是你所有问题的答案(我想知道所有问题!)但可以肯定的是,所有fromEvent Observables 都很热门。单击似乎不是因为它不是像 mousemove 这样的“连续”事件,而是在创建 Observable 时,对源(addEventListeneron 调用)的订阅只进行一次。所以很热。你可以在 herethere 运算符的源代码中看到它 - 无论事件名称或来源是什么,创建的 observable 都是 shared。

    【讨论】:

    • 然而,如果您将该点击映射到一个函数并订阅两次,那么对于同一次点击,它会通过可观察链两次。 Codepen 在这里:codepen.io/anon/pen/NqQMJR?editors=101
    • Erik,为所有这些问题添加了答案。如果您有时间,请告诉我您的想法。
    猜你喜欢
    • 1970-01-01
    • 2016-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-04
    • 1970-01-01
    相关资源
    最近更新 更多