2015-08-24 12 views
39

aver esaminato la seguente domanda SO: What are the Hot and Cold observables?Osservabili Caldo e Freddo: ci sono operatori "caldi" e "freddi"?

Riassumendo:

  • un osservabile freddo emette suoi valori quando ha un osservatore a consumare loro, cioè la sequenza di valori ricevuti da osservatori è indipendente ora dell'abbonamento. Tutti gli osservatori consumeranno la stessa sequenza di valori.
  • un osservabile caldo emette un valore indipendentemente dalle sue sottoscrizioni, ovvero i valori ricevuti dagli osservatori sono una funzione del tempo di sottoscrizione.

Eppure, sento caldo contro freddo è ancora fonte di confusione. Così qui sono le mie domande:

  • sono tutti osservabili RX freddo di default (ad eccezione dei soggetti)?

    Spesso leggo che gli eventi sono la tipica metafora degli osservabili caldi, ma ho anche letto che Rx.fromEvent(input, 'click') è un osservabile freddo (?).

  • Esistono/quali sono gli operatori Rx, che trasformano un osservabili freddo ad uno caldo osservabile (a parte publish, e share)?

    Ad esempio, come funziona con l'operatore Rx withLatestFrom? Sia cold$ un osservabile freddo che è stato sottoscritto da qualche parte. Sarà sth$.withLatestFrom(cold$,...) un caldo osservabile?

    O se lo faccio sth1$.withLatestFrom(cold$,...), sth2$.withLatestFrom(cold$,...) e sottoscrivere sth1 e sth2, sarò sempre vedere lo stesso valore per entrambi sth?

  • Ho pensato che Rx.fromEvent crea osservabili freddi, ma non è così, come menzionato in una delle risposte. Tuttavia, sono ancora sconcertato da questo comportamento: codepen.io/anon/pen/NqQMJR?editors=101. Abbonamenti diversi ricevono valori diversi dallo stesso osservabile. L'evento click non è stato condiviso?

+0

L'affermazione "Tutti gli osservatori potranno consumare la stessa sequenza di valori" sulle osservabili freddo non è vero. È vero un sacco di tempo, ma anche il semplice caso di mutare un elemento in un array che io accendo a un osservabile significa che i valori cambiano. E potrei anche creare un generatore di numeri casuali osservabile che sarebbe freddo, ma raramente ripeto i numeri di sempre. – Enigmativity

+0

Questo è corretto. In realtà questo è esattamente il caso che è presente nella codepen collegata e che ha generato le mie domande in quel momento. Spero che la mia risposta fornita sia più chiara su cosa succede in abbonamento. – user3743222

risposta

55

Sono tornato alcuni mesi dopo alla mia domanda iniziale e volevo condividere le conoscenze acquisite nel frattempo. Userò il seguente codice come un supporto spiegazione (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(){}); 

Come menzionato in una delle risposte, la definizione di un porta osservabili ad una serie di callback e il parametro di registrazione. Il flusso di dati deve essere avviato, e ciò avviene tramite la funzione subscribe. A (semplificato per illustrazione) il flusso dettagliato può essere trovato da allora in poi.

Simplified flow diagram

osservabili sono freddi per impostazione predefinita. La sottoscrizione a un osservabile comporterà la creazione di una catena di sottoscrizioni a monte. L'ultima sottoscrizione porta all'esecuzione di una funzione che gestirà una fonte ed emetterà i suoi dati al suo osservatore.

L'osservatore a sua volta emette al successivo osservatore, determinando un flusso di dati a valle, fino all'osservatore del lavello. La seguente illustrazione semplificata mostra i flussi di sottoscrizione e dati quando due abbonati si iscrivono allo stesso osservabile.

Cold observable simplified flow diagram

osservabili caldi possono essere creati utilizzando un soggetto, o attraverso il multicast operatore (e suoi derivati, vedi nota 3).

L'operatore multicast sotto il cofano fa uso di un soggetto e restituisce un osservabile collegabile. Tutti gli abbonamenti all'operatore saranno abbonamenti al soggetto interno. Quando viene chiamato connect, il soggetto interno si iscrive all'osservabile a monte e i flussi di dati a valle. I soggetti manipolano internamente un elenco di osservatori sottoscritti e dati in entrata multicast a tutti gli osservatori iscritti.

Il seguente diagramma riepiloga la situazione.

Hot observable simplified flow diagram

Alla fine, esso conta più per comprendere il flusso di dati causata dal disegno osservatore e l'attuazione degli operatori.

Ad esempio, se obs è caldo, è hotOrCold = obs.op1 freddo o caldo?Qualunque sia la risposta è:

  • se non ci sono gli abbonati a obs.op1, non ci sono dati fluirà attraverso op1. Se ci fossero abbonati a caldo obs, questo significa obs.op1 pezzi avrà forse perso di dati
  • Supponendo che op1 non è un operatore di multicast simile, sottoscrivendo due volte per hotOrCold sottoscriverà due volte per op1, e ogni valore da obs fluirà due volte attraverso op1.

Note:

  1. Queste informazioni dovrebbero essere valide per Rxjs v4. Mentre la versione 5 è passata a attraverso modifiche considerevoli, la maggior parte si applica ancora alla lettera.
  2. I flussi di annullamento, errore e completamento non sono rappresentati, come non rientrano nell'ambito della domanda. Anche gli scheduler non sono considerati . Tra le altre cose, influenzano i tempi dello flusso di dati, ma a priori non sono la direzione e il contenuto.
  3. In base al tipo di soggetto utilizzato per il multicasting, ci sono diversi operatori derivati ​​multicasting:

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

Aggiornamento: Vedi anche the following articles, here, and there) su questo argomento da Ben Lesh.

Ulteriori dettagli su argomenti possono essere trovati in questa altra domanda SO: What are the semantics of different RxJS subjects?

+0

ottimo studio. Grazie per aver condiviso questo! Potresti aggiungere una spiegazione su cosa significano "operatore di pubblicazione" e "operatore di condivisione"? –

+1

Gli operatori 'publish' sono l'operatore' multicast' con un diverso tipo di argomenti passati come parametro. Ad esempio, per 'publish', vedere qui: https://github.com/Reactive-Extensions/RxJS/blob/master/src/core/linq/observable/publish.js. Gli operatori 'share' si ottengono aggiungendo' .refCount() 'agli operatori' publish'. Ad esempio, per 'share', vedi https: // github.com/reattivi-extensions/RxJS/blob/master/src/centrali/LINQ/osservabili/share.js. – user3743222

+1

Sarebbe comunque opportuno documentare correttamente i soggetti (la documentazione corrente può essere difficile da capire). Il modo migliore che penso è se si registra una domanda specifica su tale argomento (nessun gioco di parole previsto). Sarebbe fuori portata rispondere a questo là e permetterebbe di aggiungere ulteriori dettagli. La presente risposta è già lunga e voglio concentrarla sulla domanda. La domanda potrebbe essere "quali sono le semantiche dei vari tipi di soggetti Rxjs" o qualunque altro modo si trovi per formularlo concisamente. – user3743222

2

Non è una risposta per tutte le vostre domande (Vorrei sapere tutto di loro!) Ma di sicuro, tutti fromEvent osservabili sono caldi. Il clic sembra non essere perché non è un evento "continuo" come mousemove, ma in ogni caso l'abbonamento alla fonte (addEventListener o on chiamata) viene eseguito una sola volta, quando viene creato Observable. Quindi fa caldo. È possibile visualizzarlo nel codice sorgente dell'operatore here e there - l'osservabile creato è share d indipendentemente dal nome dell'evento o dalla fonte.

+0

Eppure, se si esegue il mapping di quel clic su una funzione e si esegue l'iscrizione due volte, passa due volte alla catena osservabile per lo stesso clic. Codepen è qui: http://codepen.io/anon/pen/NqQMJR?editors=101 – user3743222

+0

Erik, ha aggiunto una risposta a tutte quelle domande. Fammi sapere cosa ne pensi se hai un po 'di tempo. – user3743222

3

values nella tua codepen è pigro - non succede niente finché qualcosa non si abbona, a quel punto attraversa e lo collega. Quindi, nel tuo esempio, sebbene tu stia iscrivendo alla stessa variabile, sta creando due flussi diversi; uno per ogni chiamata iscriversi.

Si può pensare a values come a un generatore di flussi per click con quello map allegato.

.share() alla fine di quella mappa creerebbe il comportamento che ci aspettiamo, perché è implicitamente sottoscrivendo.

6

Il tuo riassunto e la domanda collegata sono entrambi corretti, penso che la terminologia potrebbe confondervi. Vi propongo di pensare a osservabili caldi e freddi come osservabili attivi e passivi (rispettivamente).

Cioè, un osservatore attivo (caldo) emetterà elementi indipendentemente dal fatto che qualcuno si sia iscritto o meno. L'esempio canonico, ancora una volta, gli eventi di clic sul pulsante si verificano sia che qualcuno li stia ascoltando oppure no. Questa distinzione è importante perché, se, per esempio, faccio clic su un pulsante e poi mi iscrivo ai clic del pulsante (in questo ordine), non vedrò il clic del pulsante che è già successo.

Un osservabile passivo (freddo) attenderà finché un utente non esiste prima di emettere gli elementi. Immagina un pulsante in cui non puoi fare clic su di esso fino a quando qualcuno non sta ascoltando gli eventi: questo assicurerebbe che tu veda sempre ogni singolo evento.

Tutti gli oggetti osservabili Rx sono "a freddo" (o passivi) per impostazione predefinita? No, Rx.fromEvent(input, 'click') per esempio è un osservabile caldo (o attivo).

Ho anche letto che Rx.fromEvent(input, 'click') è un freddo osservabile (?)

Questo non è il caso.

Esistono operatori Rx che trasformano un osservabile a freddo in un osservabile caldo?

Il concetto di trasformare una calda (attivo) osservabile in una fredda (passivo) osservabile è questa: è necessario registrare gli eventi che accadono mentre nulla è attualmente sottoscritto e offrire tali articoli (in vari modi) per gli abbonati che vieni in futuro Un modo per farlo è usare un Subject. Ad esempio, è possibile utilizzare uno ReplaySubject per il buffer degli elementi emessi e riprodurli ai futuri abbonati.

I due operatori nominati (publish e share) utilizzano entrambi i temi internamente per offrire tale funzionalità.

Come funziona con l'operatore Rx withLatestFrom? Let cold$ essere un osservabile freddo che è stato sottoscritto. Sarà something$.withLatestFrom(cold$,...) un osservabile caldo?

Se something è un osservabile caldo, quindi sì. Se something è un osservabile a freddo, quindi no. Tornando all'esempio eventi, se something è un flusso di eventi click del pulsante:

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

O se lo faccio foo$.withLatestFrom(cold$,...), bar$.withLatestFrom(cold$,...) e sottoscrivere foo e bar, sarò sempre gli stessi valori per entrambi?

Non sempre. Ancora una volta, se foo e bar sono clic su pulsanti diversi, ad esempio, vedresti valori diversi.Inoltre, anche se fossero lo stesso pulsante, se la tua funzione di combinazione (il secondo argomento a withLatest) non restituisce lo stesso risultato per gli stessi input, non visualizzerai gli stessi valori (perché verrebbe chiamato due volte, come spiegato sotto).

Ho pensato che Rx.fromEvent crea osservabili freddi ma non è il caso, come menzionato in una delle risposte. Tuttavia, sono ancora sconcertato da questo comportamento: codepen.io/anon/pen/NqQMJR?editors=101. Abbonamenti diversi ricevono valori diversi dallo stesso osservabile. L'evento click non è stato condiviso?

Indicherò a this great answer by Enigmativity una domanda che avevo sullo stesso comportamento. Questa risposta lo spiegherà molto meglio di me, ma il succo è che la fonte (l'evento click) è "condivisa", sì, ma le tue operazioni su di essa non lo sono. Se si desidera condividere non solo l'evento click ma anche l'operazione su di esso, è necessario farlo esplicitamente.

+0

'Oppure se faccio foo $ .withLatestFrom (cold $, ...), bar $ .withLatestFrom (cold $, ...) e mi iscrivo a foo e bar, vedrò sempre gli stessi valori per entrambi?' My la domanda era più se il valore preso da 'cold $' sarà lo stesso in entrambi i casi. Il mio problema è la semantica di 'withLatestFrom'. Capisco che abbiamo due abbonamenti diversi a $ freddo, quindi mi aspetterei che venissero estratti due valori diversi, in base al momento dell'abbonamento, che distruggono la semantica dell'operatore, quindi mi aspettavo che l'operatore trasformasse 'cold $' in un flusso caldo. Ma la cosa migliore è testare e pubblicherò presto le risposte. – user3743222