J'ai passé en revue la SO question: Quels sont les observables chauds et froids?
Résumer:
Pourtant, j'ai l'impression que le chaud contre le froid est toujours une source de confusion. Donc, voici mes questions:
Tous les rx observables sont-ils froids par défaut (à l'exception des sujets)?
Je lis souvent que les événements sont la métaphore typique des observables chauds, mais je lis aussi que Rx.fromEvent(input, 'click')
est un observable froid (?).
Y a-t-il/quels sont les opérateurs Rx qui transforment un observable froid en observable chaud (à part publish
et share
)?
Par exemple, comment cela fonctionne-t-il avec l'opérateur Rx withLatestFrom
? Soit cold$
Un observable froid auquel quelque part a été souscrit. sth$.withLatestFrom(cold$,...)
sera-t-il un observable à chaud?
Ou si je fais sth1$.withLatestFrom(cold$,...), sth2$.withLatestFrom(cold$,...)
et m'abonne à sth1
Et sth2
, Verrai-je toujours la même valeur pour les deux sth
?
Je pensais que Rx.fromEvent
Crée des observables froids mais ce n'est pas le cas, comme mentionné dans l'une des réponses. Cependant, je suis toujours déconcerté par ce comportement: codepen.io/anon/pen/NqQMJR?editors=101 . Différents abonnements obtiennent des valeurs différentes du même observable. L'événement click
n'a-t-il pas été partagé?
Je reviens quelques mois plus tard à ma question initiale et souhaitais partager entre-temps les connaissances acquises. J'utiliserai le code suivant comme support d'explication ( 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(){});
Comme mentionné dans l'une des réponses, la définition d'un observable conduit à une série de rappel et d'enregistrement de paramètres. Le flux de données doit être lancé, et cela se fait via la fonction subscribe
. Un flux détaillé (simplifié pour illustration) peut être trouvé par la suite.
Les observables sont froids par défaut. La souscription à un observable se traduira par une chaîne d'abonnements en amont. Le dernier abonnement conduit à l'exécution d'une fonction qui va gérer une source et émettre ses données à son observateur.
Cet observateur émet à son tour vers l'observateur suivant, ce qui entraîne un flux de données en aval, vers l'observateur du puits. L'illustration simplifiée suivante montre l'abonnement et les flux de données lorsque deux abonnés s'abonnent au même observable.
Les observables à chaud peuvent être créés soit en utilisant un sujet, soit via l'opérateur multicast
(et ses dérivés, voir la note 3 ci-dessous).
L'opérateur multicast
sous le capot utilise un sujet et renvoie un observable connectable. Tous les abonnements à l'opérateur seront des abonnements au sujet intérieur. Lorsque connect
est appelé, le sujet interne s'abonne à l'observable en amont et les données circulent en aval. Les sujets manipulent en interne une liste d'observateurs abonnés et multidiffusent les données entrantes vers tous les observateurs abonnés.
Le diagramme suivant résume la situation.
En fin de compte, il importe davantage de comprendre le flux de données provoqué par le modèle d'observation et la mise en œuvre des opérateurs.
Par exemple, si obs
est chaud, est hotOrCold = obs.op1
froid ou chaud? Quelle que soit la réponse:
obs.op1
, aucune donnée ne transite par op1
. S'il y avait des abonnés à hot obs
, cela signifie obs.op1
aura probablement perdu des donnéesop1
n'est pas un opérateur de type multidiffusion, s'abonner deux fois à hotOrCold
s'abonnera deux fois à op1
, et chaque valeur de obs
circulera deux fois dans op1
.Remarques :
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
Mise à jour : Voir aussi les articles suivants, ici, et là ) à ce sujet par Ben Lesh.
D'autres détails sur les sujets peuvent être trouvés dans cette autre SO question: Quelle est la sémantique des différents sujets RxJS?
Votre résumé et la question liée sont tous deux corrects, je pense que la terminologie peut vous prêter à confusion. Je vous propose de considérer les observables chauds et froids comme des observables actifs et passifs (respectivement).
Autrement dit, un observable actif (à chaud) émettra des éléments, que quelqu'un se soit abonné ou non. L'exemple canonique, encore une fois, les événements de clic de bouton se produisent, que quelqu'un les écoute ou non. Cette distinction est importante car, si, par exemple, je clique sur un bouton, puis je m'abonne aux clics sur les boutons (dans cet ordre), je ne verrai pas le clic sur le bouton qui s'est déjà produit.
Un observable passif (froid) attendra qu'un abonné existe avant d'émettre des éléments. Imaginez un bouton sur lequel vous ne pouvez pas cliquer dessus jusqu'à ce que quelqu'un écoute les événements - cela garantirait que vous voyez toujours chaque événement de clic.
Tous les observables Rx sont-ils "froids" (ou passifs) par défaut? Non, Rx.fromEvent(input, 'click')
par exemple est un observable à chaud (ou actif).
J'ai également lu que
Rx.fromEvent(input, 'click')
est un observable à froid (?)
Ce n'est pas le cas.
Y a-t-il des opérateurs Rx qui transforment un observable froid en observable chaud?
Le concept de transformer un observable chaud (actif) en observable froid (passif) est le suivant: vous devez enregistrer les événements qui se produisent alors que rien n'est souscrit et proposer ces éléments (de diverses manières) aux abonnés qui viendront à l'avenir. Une façon de le faire est d'utiliser un Subject . Par exemple, vous pouvez utiliser un ReplaySubject
pour mettre en mémoire tampon les éléments émis et les relire aux futurs abonnés.
Les deux opérateurs que vous avez nommés (publish
et share
) utilisent tous deux des sujets en interne pour offrir cette fonctionnalité.
Comment ça marche avec l'opérateur Rx
withLatestFrom
? Soitcold$
Un observable froid auquel vous avez souscrit.something$.withLatestFrom(cold$,...)
sera-t-il un observable à chaud?
Si something
est un observable à chaud, alors oui. Si something
est observable à froid, alors non. Pour revenir à l'exemple d'événements, si something
est un flux d'événements de clic de bouton:
var clickWith3 = Rx.fromEvent(input, 'click')
.withLatest(Rx.Observable.from([1, 2, 3]);
Ou si je fais
foo$.withLatestFrom(cold$,...), bar$.withLatestFrom(cold$,...)
et que je m'abonne àfoo
etbar
, verrai-je toujours les mêmes valeurs pour les deux?
Pas toujours. Encore une fois, si foo
et bar
sont des clics sur différents boutons par exemple, alors vous verrez des valeurs différentes. De même, même s'il s'agissait du même bouton, si votre fonction de combinaison (le deuxième argument de withLatest
) ne renvoie pas le même résultat pour les mêmes entrées, vous ne verrez pas les mêmes valeurs (car cela être appelé deux fois, comme expliqué ci-dessous).
Je pensais que
Rx.fromEvent
Crée des observables froids mais ce n'est pas le cas, comme mentionné dans l'une des réponses. Cependant, je suis toujours déconcerté par ce comportement: codepen.io/anon/pen/NqQMJR?editors=101 . Différents abonnements obtiennent des valeurs différentes du même observable. L'événementclick
n'a-t-il pas été partagé?
Je vais vous indiquer cette excellente réponse d'Enigmativity à une question que j'avais sur le même comportement. Cette réponse l'expliquera beaucoup mieux que moi, mais l'essentiel est que la source (l'événement click) est "partagée", oui, mais vos opérations dessus ne le sont pas. Si vous souhaitez partager non seulement l'événement de clic, mais également l'opération sur celui-ci, vous devrez le faire explicitement.
values
dans votre codepen est paresseux - rien ne se passe jusqu'à ce que quelque chose s'abonne, à quel point il passe et le connecte. Ainsi, dans votre exemple, bien que vous vous abonniez à la même variable, cela crée deux flux différents; un pour chaque appel d'abonnement.
Vous pouvez considérer values
comme un générateur de flux pour click
avec ce map
attaché.
.share()
à la fin de cette carte créerait le comportement que nous attendons, car il est implicitement souscrit.
Ce n'est pas une réponse à toutes vos questions (j'aimerais toutes les connaître!) Mais pour sûr, tous les fromEvent
observables sont chauds. Le clic semble ne pas l'être car ce n'est pas un événement "continu" comme mousemove, mais de toute façon l'abonnement à la source (appel addEventListener
ou on
) n'est effectué qu'une seule fois, lorsque Observable est créé. Il fait donc chaud. Vous pouvez le voir dans le code source de l'opérateur ici et là - l'observable créé est share
d quel que soit le nom ou la source de l'événement.