Observables fríos y calientes: ¿hay operadores "fríos" y "calientes"?

Resuelto user3743222 asked hace 9 años • 4 respuestas

Revisé la siguiente pregunta SO: ¿Cuáles son los observables fríos y calientes?

Para resumir:

  • un observable frío emite sus valores cuando tiene un observador que los consume, es decir, la secuencia de valores recibidos por los observadores es independiente del tiempo de suscripción. Todos los observadores consumirán la misma secuencia de valores.
  • un observable caliente emite valor independientemente de sus suscripciones, es decir, los valores recibidos por los observadores son función del tiempo de suscripción.

Sin embargo, siento que el calor versus el frío sigue siendo una fuente de confusión. Asi que aqui están mis preguntas:

  • ¿Todos los observables de rx están fríos de forma predeterminada (con la excepción de los sujetos)?

    A menudo leo que los eventos son la metáfora típica de los observables calientes, pero también leo que Rx.fromEvent(input, 'click')son observables fríos (?).

  • ¿Existen/cuáles son los operadores Rx que convierten un observable frío en un observable caliente (aparte de publishy share)?

    Por ejemplo, ¿cómo funciona con el operador Rx withLatestFrom? Sea cold$un observable frío al que se haya suscrito en algún lugar. ¿ Será sth$.withLatestFrom(cold$,...)un observable caliente?

    O si lo hago sth1$.withLatestFrom(cold$,...), sth2$.withLatestFrom(cold$,...)y me suscribo a sth1y sth2, ¿veré siempre el mismo valor para ambos sth?

  • Pensé que Rx.fromEventcrea observables fríos, pero ese no es el caso, como se menciona en una de las respuestas. Sin embargo, todavía estoy desconcertado por este comportamiento: https://codepen.io/anon/pen/NqQMJR?editors=101 . Diferentes suscripciones obtienen valores diferentes del mismo observable. ¿ No se clickcompartió el evento?

user3743222 avatar Aug 25 '15 02:08 user3743222
Aceptado

Vuelvo unos meses más tarde a mi pregunta original y mientras tanto quería compartir los conocimientos adquiridos. Usaré el siguiente código como soporte explicativo ( 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(){});

Como se menciona en una de las respuestas, definir un observable conduce a una serie de devoluciones de llamada y registro de parámetros. El flujo de datos debe iniciarse, y eso se hace a través de la subscribefunción. A continuación se puede encontrar un flujo detallado (simplificado a modo de ilustración).

Diagrama de flujo simplificado

Los observables son fríos por defecto. La suscripción a un observable dará como resultado una cadena ascendente de suscripciones. La última suscripción conduce a la ejecución de una función que manejará una fuente y emitirá sus datos a su observador.

Ese observador, a su vez, emite al siguiente observador, lo que da como resultado un flujo de datos descendente, hasta el observador sumidero. La siguiente ilustración simplificada muestra los flujos de datos y suscripción cuando dos suscriptores se suscriben al mismo observable.

Diagrama de flujo simplificado observable en frío.

Los observables calientes se pueden crear utilizando un sujeto o a través del multicastoperador (y sus derivados, consulte la Nota 3 a continuación).

El multicastoperador oculto utiliza un sujeto y devuelve un observable conectable. Todas las suscripciones al operador serán suscripciones al sujeto interno. Cuando connectse llama, el sujeto interno se suscribe al observable ascendente y los datos fluyen descendentes. Los sujetos manipulan internamente una lista de observadores suscritos y transmiten datos entrantes a todos los observadores suscritos.

El siguiente diagrama resume la situación.

Diagrama de flujo simplificado observable en caliente

Al final, es más importante comprender el flujo de datos causado por el patrón del observador y la implementación de los operadores.

Por ejemplo, si obshace calor, ¿hace hotOrCold = obs.op1frío o calor? Cualquiera que sea la respuesta:

  • si no hay suscriptores obs.op1, no fluirá ningún dato op1. Si hubiera suscriptores de hot obs, eso significa que obs.op1posiblemente se habrán perdido datos
  • Suponiendo que op1no sea un operador de tipo multidifusión, suscribirse dos veces a hotOrColdse suscribirá dos veces a op1y cada valor de obsfluirá dos veces a través de op1.

Notas:

  1. Esta información debería ser válida para Rxjs v4. Si bien la versión 5 ha pasado por cambios considerables, la mayor parte todavía se aplica palabra por palabra.
  2. Los flujos de cancelación de suscripción, errores y finalización no están representados, ya que no están dentro del alcance de la pregunta. Los programadores tampoco se tienen en cuenta. Entre otras cosas, influyen en el momento del flujo de datos, pero a priori no en su dirección y contenido.
  3. Según el tipo de tema utilizado para la multidifusión, existen diferentes operadores de multidifusión derivados:

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

Actualización : consulte también los siguientes artículos, aquí y allá ) sobre ese tema de Ben Lesh.

Se pueden encontrar más detalles sobre los temas en esta otra pregunta SO: ¿ Cuáles son la semántica de los diferentes temas de RxJS?

user3743222 avatar Jan 08 '2016 04:01 user3743222

Su resumen y la pregunta vinculada son correctos, creo que la terminología puede resultar confusa. Le propongo que piense en los observables fríos y calientes como observables activos y pasivos (respectivamente).

Es decir, un observable activo (caliente) emitirá elementos independientemente de que alguien se haya suscrito o no. En el ejemplo canónico, nuevamente, los eventos de clic en un botón ocurren ya sea que alguien los esté escuchando o no. Esta distinción es importante porque, si, por ejemplo, hago clic en un botón y luego me suscribo a los clics en el botón (en ese orden), no veré el clic en el botón que ya ocurrió.

Un observable pasivo (frío) esperará hasta que exista un suscriptor antes de emitir elementos. Imagine un botón en el que no puede hacer clic en él hasta que alguien esté escuchando los eventos; esto garantizaría que siempre vea todos y cada uno de los eventos de clic.

¿Todos los observables de Rx son "fríos" (o pasivos) de forma predeterminada? No, Rx.fromEvent(input, 'click')por ejemplo, es un observable caliente (o activo).

También leí que Rx.fromEvent(input, 'click')es un frío observable (?)

Ese no es el caso.

¿Existen operadores de Rx que convierten un observable frío en un observable caliente?

El concepto de convertir un observable caliente (activo) en un observable frío (pasivo) es el siguiente: es necesario registrar los eventos que suceden mientras no hay nada suscrito y ofrecer esos elementos (de varias maneras) a los suscriptores que lleguen en el futuro. Una forma de hacerlo es utilizar un Asunto . Por ejemplo, podría utilizar a ReplaySubjectpara almacenar en búfer los elementos emitidos y reproducirlos para futuros suscriptores.

Los dos operadores que nombró ( publishy share) utilizan sujetos internamente para ofrecer esa funcionalidad.

¿Cómo funciona con el operador Rx withLatestFrom? Sea cold$un observable frío al que se ha suscrito. ¿ Será something$.withLatestFrom(cold$,...)un observable caliente?

Si somethinges un observable candente, entonces sí. Si somethingse observa un resfriado, entonces no. Volviendo al ejemplo de eventos, si somethinges una secuencia de eventos de clic de botón:

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

O si lo hago foo$.withLatestFrom(cold$,...), bar$.withLatestFrom(cold$,...)y me suscribo a fooy bar, ¿veré siempre los mismos valores para ambos?

No siempre. Nuevamente, si fooy barse hacen clic en botones diferentes, por ejemplo, verá valores diferentes. Además, incluso si fueran el mismo botón, si su función de combinación (el segundo argumento de withLatest) no devuelve el mismo resultado para las mismas entradas, entonces no vería los mismos valores (porque se llamaría dos veces, como se explicó). abajo).

Pensé que Rx.fromEventcrea observables fríos, pero ese no es el caso, como se menciona en una de las respuestas. Sin embargo, todavía estoy desconcertado por este comportamiento: codepen.io/anon/pen/NqQMJR?editors=101 . Diferentes suscripciones obtienen valores diferentes del mismo observable. ¿ No se clickcompartió el evento?

Les señalaré esta gran respuesta de Enigmativity a una pregunta que tenía sobre el mismo comportamiento. Esa respuesta lo explicará mucho mejor que yo, pero lo esencial es que la fuente (el evento de clic) está "compartida", sí, pero sus operaciones en ella no. Si desea compartir no solo el evento de clic sino también la operación realizada en él, deberá hacerlo explícitamente.

Whymarrh avatar Sep 06 '2015 22:09 Whymarrh

valuesen su codepen es vago: no sucede nada hasta que algo se suscribe, momento en el que lo ejecuta y lo conecta. Entonces, en su ejemplo, aunque se suscribe a la misma variable, se crean dos flujos diferentes; uno para cada llamada de suscripción.

Puedes considerarlo valuescomo un generador de transmisiones con clickeso mapadjunto.

.share()al final de ese mapa crearía el comportamiento que esperamos, porque está implícitamente suscribiéndose.

electrichead avatar Sep 03 '2015 02:09 electrichead