javascript Hot 和 Cold observables:是否有“hot”和“cold”运算符?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32190445/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 14:57:47  来源:igfitidea点击:

Hot and Cold observables: are there 'hot' and 'cold' operators?

javascriptangularrxjsreactive-programmingrxjs5

提问by user3743222

I reviewed the following SO question: What are the Hot and Cold observables?

我回顾了以下 SO 问题: Hot 和 Cold observables 是什么?

To summarize:

总结一下:

  • a cold observable emits its values when it has an observer to consume them, i.e. the sequence of values received by observers is independent of time of subscription. All observers will consume the same sequence of values.
  • a hot observable emits value independently of its subscriptions, i.e. the values received by observers are a function of the time of subscription.
  • 当一个冷的 observable 有一个观察者来消费它们时,它会发出它的值,即观察者接收到的值的序列与订阅时间无关。所有观察者都将使用相同的值序列。
  • 热 observable 发出与其订阅无关的值,即观察者接收到的值是订阅时间的函数。

Yet, I feel like hot vs. cold is still a source of confusion. So here are my questions:

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

  • Are all rx observables cold by default (with the exception of subjects)?

    I often read that events are the typical metaphor for hot observables, but I also read that Rx.fromEvent(input, 'click')is a cold observable(?).

  • Are there/what are the Rx operators which turn a cold observables into a hot observable (apart from publish, and share)?

    For instance, how does it work with Rx operator withLatestFrom? Let cold$be a cold observable which has somewhere been subscribed to. Will sth$.withLatestFrom(cold$,...)be a hot observable?

    Or if I do sth1$.withLatestFrom(cold$,...), sth2$.withLatestFrom(cold$,...)and subscribe to sth1and sth2, will I always see the same value for both sth?

  • I thought Rx.fromEventcreates cold observables but that is not the case, as mentioned in one of the answers. However, I am still baffled by this behaviour: https://codepen.io/anon/pen/NqQMJR?editors=101. Different subscriptions get different values from the same observable. Wasn't the clickevent shared?

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

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

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

    例如,它如何与 Rx 运算符一起工作withLatestFrom?让我们cold$成为一个已在某处订阅的冷 observable。会sth$.withLatestFrom(cold$,...)是一个热门的 observable 吗?

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

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

回答by user3743222

I am coming back a few months later to my original question and wanted to share the gained knowledge in the meanwhile. I will use the following code as an explanation support (jsfiddle):

几个月后我又回到了我原来的问题,同时想分享所获得的知识。我将使用以下代码作为解释支持(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(){});

As mentioned in one of the answers, defining an observable leads to a series of callback and parameter registration. The data flow has to be kicked in, and that is done via the subscribefunction. A (simplified for illustration) detailed flow can be find thereafter.

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

Simplified flow diagram

简化流程图

Observables are cold by default. Subscribing to an observable will result in an upstream chain of subscriptions taking place. The last subscription leads to the execution of a function which will handle a source and emit its data to its observer.

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

That observer in its turn emits to the next observer, resulting in a downstream flow of data, down to the sink observer. The following simplified illustration shows subscription and data flows when two subscribers subscribe to the same observable.

该观察者依次向下一个观察者发送数据,从而产生下游数据流,直至下沉观察者。下面的简化图显示了当两个订阅者订阅同一个 observable 时的订阅和数据流。

Cold observable simplified flow diagram

Cold observable 简化流程图

Hot observables can be created either by using a subject, or through the multicastoperator (and its derivatives, see Note 3 below).

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

The multicastoperator under the hood makes use of a subject and returns a connectable observable. All subscriptions to the operator will be subscriptions to the inner subject. When connectis called, the inner subject subscribes to the upstream observable, and data flows downstream. Subjects manipulate internally a list of subscribed observers and multicast incoming data to all subscribed observers.

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

The following diagram summarizes the situation.

下图总结了这种情况。

Hot observable simplified flow diagram

Hot observable 简化流程图

In the end, it matters more to understand the flow of data caused by the observer pattern and the implementation of the operators.

最后,更重要的是理解观察者模式导致的数据流和运算符的实现。

For instance, if obsis hot, is hotOrCold = obs.op1cold or hot? Whatever the answer is :

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

  • if there are no subscribers to obs.op1, no data will flow through op1. If there were subscribers to hot obs, that means obs.op1will have possibly lost pieces of data
  • supposing that op1is not a multicast-like operator, subscribing twice to hotOrColdwill subscribe twice to op1, and every value from obswill flow twice through op1.
  • 如果没有订阅者obs.op1,则没有数据流过op1。如果有订阅者到 hot obs,那意味着obs.op1可能会丢失一些数据。
  • 假设它op1不是类似多播的运算符,订阅两次 tohotOrCold将订阅两次op1,并且每个值 fromobs将流过两次op1

Notes :

注意事项:

  1. This information should be valid for Rxjs v4. While the version 5 has gone through considerable changes, most of it still applies verbatim.
  2. Unsubscription, error and completion flows are not represented, as they are not in the scope of the question. Schedulers are also not taken into account. Among other things, they influence the timing of the data flow, but a priori not its direction and content.
  3. According to the type of subject used for multicasting, there are different derived multicasting operators:
  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

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

Update: See also the following articles, here, and there) on that subject by Ben Lesh.

更新:另请参阅本·莱什 (Ben Lesh)撰写的有关该主题的以下文章 (here和 there)。

Further details on subjects can be found in this other SO question : What are the semantics of different RxJS subjects?

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

回答by Whymarrh

Your summary, and the linked question are both correct, I think the terminology may be confusing you. I propose you think of hot and cold observables as active and passive observables (respectively).

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

That is, an active (hot) observable will be emitting items whether someone has subscribed or not. The canonical example, again, button click events happen whether someone is listening to them or not. This distinction is important because, if, for example, I click a button and then subscribe to button clicks (in that order), I will not see the button click that has already happened.

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

A passive (cold) observable will wait until a subscriber exists before emitting items. Imagine a button where you cannot click on it until someone is listening to the events—this would ensure that you always see each and every click event.

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

Are all Rx observables "cold" (or passive) by default? No, Rx.fromEvent(input, 'click')for example is a hot (or active) observable.

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

I also read that Rx.fromEvent(input, 'click')is a cold observable(?)

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

That is not the case.

事实并非如此。

Are there Rx operators which turn a cold observable into a hot observable?

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

The concept of turning a hot (active) observable into a cold (passive) observable is this: you need to record the events that happen while nothing is subscribed and offer those items (in various ways) to subscribers that come along in the future. One way to do this is to use a Subject. For example, you could use a ReplaySubjectto buffer up items emitted and replay them to future subscribers.

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

The two operators you named (publishand share) both use subjects internally to offer that functionality.

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

How does it work with Rx operator withLatestFrom? Let cold$be a cold observable which has been subscribed to. Will something$.withLatestFrom(cold$,...)be a hot observable?

它如何与 Rx 运算符一起使用withLatestFrom?让我们cold$成为一个已订阅的冷 observable。会something$.withLatestFrom(cold$,...)是一个热门的 observable 吗?

If somethingis a hot observable, then yes. If somethingis a cold observable, then no. Going back to the events example, if somethingis a stream of button click events:

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

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

Or if I do foo$.withLatestFrom(cold$,...), bar$.withLatestFrom(cold$,...)and subscribe to fooand bar, will I always see the same values for both?

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

Not always. Again, if fooand barare clicks on different buttons for example, then you would see different values. As well, even if they were the same button, if your combination function (the 2nd argument to withLatest) does not return the same result for the same inputs, then you would not see the same values (because it would be called twice, as explained below).

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

I thought Rx.fromEventcreates cold observables but that is not the case, as mentioned in one of the answers. However, I am still baffled by this behaviour: codepen.io/anon/pen/NqQMJR?editors=101. Different subscriptions get different values from the same observable. Wasn't the clickevent shared?

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

I'll point you to this great answer by Enigmativityto a question I had about the same behaviour. That answer will explain it a lot better than I can, but the gist of it is that the source (the click event) is "shared", yes, but your operations on it are not. If you want to share not just the click event but also the operation on it, you will need to do so explicitly.

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

回答by electrichead

valuesin your codepen is lazy - nothing happens until something subscribes, at which point it runs through and wires it up. So in your example, although you are subscribing to the same variable, it is creating two different streams; one for each subscribe call.

values在您的 codepen 中是惰性的 - 直到某些东西订阅之后才会发生任何事情,此时它会运行并连接它。所以在你的例子中,虽然你订阅了同一个变量,但它创建了两个不同的流;每个订阅调用一个。

You can think of valuesas being a generator of streams for clickwith that mapattached.

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

.share()on the end of that map would create the behaviour we expect, because it is implicitly subscribing.

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

回答by Eryk Napiera?a

It's not an answer for all your questions (I would like to know all of them!) but for sure, all fromEventObservables are hot. Click seems to be not because it's not "continous" event like mousemove, but anyway subscription to the source (addEventListeneror oncall) is done only once, when Observable is created. So it's hot. You can see it in source code of the operator hereand there- created observable is shared no matter what is the event name or source.

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