javascript - son - Observables en caliente y en frío: ¿hay operadores ''en caliente'' y ''en frío''?
fuentes de luz natural y artificial para niños (4)
Revisé la siguiente pregunta SO: ¿Cuáles son los observables de frío y calor?
Para resumir:
- un observable frío emite sus valores cuando tiene un observador para consumirlos, 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 en caliente emite un valor independientemente de sus suscripciones, es decir, los valores recibidos por los observadores son una función del momento de la suscripción.
Sin embargo, siento que el calor y el frío siguen siendo una fuente de confusión. Asi que aqui están mis preguntas:
-
¿Todos los observables de rx están fríos por defecto (con la excepción de los sujetos)?
A menudo leo que los eventos son la metáfora típica de los observables en caliente, pero también leo que
Rx.fromEvent(input, ''click'')
es un observable en frío (?). -
¿Hay / qué son los operadores de Rx que convierten un observable frío en un observable caliente (aparte de
publish
yshare
)?Por ejemplo, ¿cómo funciona con el operador Rx
withLatestFrom
? Deje quecold$
sea un observable en frío al que se haya suscrito en algún lugar. ¿sth$.withLatestFrom(cold$,...)
será un observable caliente?O si hago
sth1$.withLatestFrom(cold$,...), sth2$.withLatestFrom(cold$,...)
y me suscribo asth1
ysth2
, ¿siempre veré el mismo valor para ambas cosas? -
Pensé que
Rx.fromEvent
crea 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 . Suscripciones diferentes obtienen valores diferentes del mismo observable. ¿No se compartió el evento declick
?
No es una respuesta para todas sus preguntas (¡me gustaría
fromEvent
todas!), Pero sin duda, todas
fromEvent
Observables del
fromEvent
están de moda.
El clic parece no ser porque no es un evento "continuo" como mousemove, sino que la suscripción a la fuente (
addEventListener
o
on
call) se realiza solo una vez, cuando se crea Observable.
Entonces hace calor.
Puede verlo en el código fuente del operador
here
y
there
: el observable creado es
share
d sin importar cuál sea el nombre o la fuente del evento.
Regresaré unos meses más tarde a mi pregunta original y, mientras tanto, quería compartir el conocimiento adquirido. jsfiddle 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, la definición de un observable conduce a una serie de devolución de llamada y registro de parámetros.
El flujo de datos debe iniciarse, y eso se hace a través de la función de
subscribe
.
A continuación se puede encontrar un flujo detallado (simplificado para ilustración).
Los observables están fríos por defecto. Suscribirse a un observable dará como resultado una cadena ascendente de suscripciones. La última suscripción lleva 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 resulta en un flujo de datos aguas abajo, hacia el observador de sumidero. La siguiente ilustración simplificada muestra los flujos de suscripción y datos cuando dos suscriptores se suscriben al mismo observable.
Los observables en caliente se pueden crear mediante el uso de un sujeto o mediante el operador de
multicast
(y sus derivados, consulte la Nota 3 a continuación).
El operador de
multicast
debajo del capó hace uso de un sujeto y devuelve un observable conectable.
Todas las suscripciones al operador serán suscripciones al sujeto interno.
Cuando se llama a
connect
, el sujeto interno se suscribe al observable en sentido ascendente y los datos fluyen en sentido descendente.
Los sujetos manipulan internamente una lista de observadores suscritos y datos entrantes de multidifusión a todos los observadores suscritos.
El siguiente diagrama resume la situación.
Al final, es más importante comprender el flujo de datos causado por el patrón de observación y la implementación de los operadores.
Por ejemplo, si
obs
está caliente, ¿
hotOrCold = obs.op1
frío o caliente?
Cualquiera que sea la respuesta es:
-
Si no hay suscriptores a
obs.op1
, noobs.op1
datos a través deop1
. Si hubo suscriptores a hotobs
, eso significa queobs.op1
posiblemente habrá perdido datos -
suponiendo que
op1
no es un operador de multidifusión, suscribirse dos veces ahotOrCold
se suscribirá dos veces aop1
, y cada valor deobs
fluirá dos veces a través deop1
.
Notas:
- Esta información debe ser válida para Rxjs v4. Si bien la versión 5 ha experimentado cambios considerables, la mayor parte todavía se aplica textualmente.
- Los flujos de cancelación de suscripción, error 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.
- 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 : Vea 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 las semánticas de los diferentes temas RxJS?
Su resumen y la pregunta vinculada son correctas, creo que la terminología puede confundirlo. Le propongo que piense en los observables fríos y calientes como observables activos y pasivos (respectivamente).
Es decir, un observable activo (activo) emitirá elementos independientemente de si alguien se ha suscrito o no. El ejemplo canónico, una vez más, los eventos de clic de botón ocurren si alguien los está escuchando o no. Esta distinción es importante porque, por ejemplo, si hago clic en un botón y luego me suscribo a los clics de botón (en ese orden), no veré el clic de botón que ya ha sucedido.
Un observable pasivo (frío) esperará hasta que exista un suscriptor antes de emitir elementos. Imagine un botón donde 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) por defecto?
No,
Rx.fromEvent(input, ''click'')
por ejemplo, es un observable activo (o activo).
También leí que
Rx.fromEvent(input, ''click'')
es un observable en frío (?)
Ese no es el caso.
¿Hay operadores 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: debe registrar los eventos que suceden mientras nada está suscrito y ofrecer esos elementos (de varias maneras) a los suscriptores que se presenten en el futuro.
Una forma de hacer esto es usar un
Subject
.
Por ejemplo, podría usar un
ReplaySubject
para almacenar los elementos emitidos y reproducirlos en futuros suscriptores.
Los dos operadores que nombró (
publish
y
share
) usan temas internamente para ofrecer esa funcionalidad.
¿Cómo funciona con el operador Rx
withLatestFrom
? Deje quecold$
sea un observable en frío al que se haya suscrito. ¿something$.withLatestFrom(cold$,...)
será observable?
Si
something
es observable, entonces sí.
Si
something
es un frío observable, entonces no.
Volviendo al ejemplo de eventos, si
something
es una secuencia de eventos de clic de botón:
var clickWith3 = Rx.fromEvent(input, ''click'')
.withLatest(Rx.Observable.from([1, 2, 3]);
O si hago
foo$.withLatestFrom(cold$,...), bar$.withLatestFrom(cold$,...)
y me suscribo afoo
ybar
, ¿siempre veré los mismos valores para ambos?
No siempre.
Nuevamente, si
foo
y
bar
son clics en diferentes botones, por ejemplo, entonces vería diferentes valores.
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 explicado a continuación).
Pensé que
Rx.fromEvent
crea 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 . Suscripciones diferentes obtienen valores diferentes del mismo observable. ¿No se compartió el evento declick
?
Te diré a esta gran respuesta de Enigmativity a una pregunta que tenía sobre el mismo comportamiento. Esa respuesta lo explicará mucho mejor de lo que puedo, pero lo esencial es que la fuente (el evento de clic) está "compartida", sí, pero sus operaciones no lo son. Si desea compartir no solo el evento de clic, sino también la operación en él, deberá hacerlo explícitamente.
values
en su codepen son vagos: no sucede nada hasta que algo se suscribe, momento en el que se ejecuta y lo conecta.
Entonces, en su ejemplo, aunque se está suscribiendo a la misma variable, está creando dos flujos diferentes;
uno para cada llamada de suscripción.
Puede pensar en los
values
como un generador de secuencias para
click
con ese
map
adjunto.
.share()
al final de ese mapa crearía el comportamiento que esperamos, porque se está suscribiendo implícitamente.