Oggetto vs BehaviorSubject vs ReplaySubject in Angular


124

Ho cercato di capire quei 3:

Oggetto , soggetto del comportamento e soggetto della riproduzione . Vorrei usarli e sapere quando e perché, quali sono i vantaggi di usarli e sebbene abbia letto la documentazione, guardato i tutorial e cercato su Google non sono riuscito a dare un senso a questo.

Allora qual è il loro scopo? Un caso del mondo reale sarebbe molto apprezzato in quanto non deve nemmeno codificare.

Preferirei una spiegazione pulita non solo "a + b => c sei iscritto a ...."

Grazie


1
C'è già una domanda con comportamento soggetto con osservabile; stackoverflow.com/questions/39494058/… e la documentazione sull'argomento replay è chiara imo github.com/Reactive-Extensions/RxJS/blob/master/doc/api/…
eko

C'è una presentazione relativamente approfondita degli argomenti in Rxjs in questa risposta , che integra bene la risposta di peeksilet. Ciò include anche dettagli importanti sul comportamento dopo la risoluzione, quindi è bene dare un'occhiata.
user3743222

Risposte:


278

Dipende davvero dal comportamento e dalla semantica. Con un

  • Subject- un abbonato riceverà solo i valori pubblicati che sono stati emessi dopo l'abbonamento. Chiediti, è questo quello che vuoi? L'abbonato deve sapere qualcosa sui valori precedenti? In caso contrario, puoi usarlo, altrimenti scegli uno degli altri. Ad esempio, con la comunicazione da componente a componente. Supponi di avere un componente che pubblica eventi per altri componenti con un clic del pulsante. Puoi utilizzare un servizio con un soggetto da comunicare.

  • BehaviorSubject- l'ultimo valore viene memorizzato nella cache. Un abbonato riceverà l'ultimo valore al momento dell'abbonamento iniziale. La semantica per questo argomento è rappresentare un valore che cambia nel tempo. Ad esempio un utente connesso. L'utente iniziale potrebbe essere un utente anonimo. Ma una volta che un utente effettua l'accesso, il nuovo valore è lo stato utente autenticato.

    La BehaviorSubjectviene inizializzato con un valore iniziale. Questo a volte è importante per le preferenze di codifica. Supponiamo ad esempio di inizializzarlo con un file null. Quindi nel tuo abbonamento, devi fare un controllo nullo. Forse OK, o forse fastidioso.

  • ReplaySubject- può memorizzare fino a un numero specificato di emissioni. Tutti gli abbonati riceveranno tutti i valori memorizzati nella cache al momento della sottoscrizione. Quando avresti bisogno di questo comportamento? Onestamente, non ho avuto bisogno di tale comportamento, ad eccezione del seguente caso:

    Se si inizializza un ReplaySubjectcon una dimensione del buffer di 1, allora si comporta proprio come un file BehaviorSubject. L'ultimo valore viene sempre memorizzato nella cache, quindi si comporta come un valore che cambia nel tempo. Con questo, non è necessario un nullcontrollo come nel caso BehaviorSubjectdell'inizializzato con a null. In questo caso, al sottoscrittore non viene mai emesso alcun valore fino alla prima pubblicazione.

Quindi dipende davvero dal comportamento che ti aspetti (come per quale usare). La maggior parte delle volte probabilmente vorrai usare una BehaviorSubjectperché ciò che vuoi veramente rappresentare è quella semantica del "valore nel tempo". Ma personalmente non vedo nulla di sbagliato nella sostituzione di ReplaySubjectinizializzato con 1.

Quello che vuoi evitare è usare il vanilla Subjectquando ciò di cui hai veramente bisogno è un comportamento di caching. Ad esempio, stai scrivendo una guardia di rotta o una risoluzione. Recuperate alcuni dati in quella guardia e impostateli in un servizio Subject. Quindi nel componente instradato ti iscrivi al servizio soggetto per cercare di ottenere quel valore che è stato emesso nella guardia. OOPs. Dov'è il valore? Era già stato emesso, DUH. Usa un oggetto di "cache"!

Guarda anche:


1
Questo è breve e facile da capire le differenze. Quando il valore cambia nel servizio e anche i componenti cambiano il valore visualizzato, BehaviourSubjects o Replay Subject è la soluzione.
Saiyaff Farouk

1
Grazie! ReplaySubjectcon una dimensione del buffer di 1 era esattamente ciò di cui avevo bisogno. Avevo una guardia di rotta che aveva bisogno del valore, ma doveva aspettare la prima emissione. Quindi a BehaviorSubjectnon lo stava tagliando, poiché non volevo un valore iniziale ( nullnon funzionava nemmeno perché lo stavo usando per indicare uno stato)
menehune23

1
@ menehune23 Ho anche bisogno di ReplaySubject per una resolveclasse di guardia angolare . Il mio servizio dati potrebbe essere asincrono o sincrono (se i dati fossero già stati recuperati). Se era sincrono, Subject.next () veniva attivato prima che la resolvefunzione fosse restituita e sottoscritta internamente da Angular. BehaviourSubject potrebbe funzionare, ma dovrei chiamare esplicitamente complete()e aggiungere anche nullcontrolli per il valore iniziale. Ciò che ha funzionato era nuovo ReplaySubject<DataType>(1) eresolveSubject.asObservable().take(1).map(....)
Drenai

1
Sto usando un ReplaySubject con dimensione del buffer di 1 ma per qualche motivo quando ricevo un Observable con .asObservable()Observable invio un valore di nullagli abbonati prima di chiamare next()il mio ReplaySubject. Pensavo che non avrebbe dovuto avere un valore iniziale a differenza di BehaviorSubject?
Kyle V.

2
Penso che un esempio abbastanza semplice che potresti citare per l'oggetto del replay potrebbe essere uno scenario di "chatroom" o lobby di gioco in cui vuoi che i nuovi partecipanti vedano gli ultimi 10 messaggi.
James,

16

Un pratico riepilogo dei diversi tipi osservabili, denominazione non intuitiva che conosco lol .

  • Subject - Un abbonato riceverà solo i valori pubblicati dopo la sottoscrizione.
  • BehaviorSubject - I nuovi abbonati ottengono l'ultimo valore pubblicato O il valore iniziale immediatamente dopo l'iscrizione.
  • ReplaySubject - I nuovi abbonati ottengono tutti i valori precedentemente pubblicati immediatamente dopo l'iscrizione

1-n valori pubblicati? Quindi se ci fossero 2 valori pubblicati un ReplaySubject produrrebbe -1 valori pubblicati ???
Jason Cheng

@JasonCheng no recupera tutti i valori pubblicati in precedenza al momento dell'iscrizione, aggiorna la risposta :)
Ricky Boyce

10
  1. Oggetto : Al momento della sottoscrizione ottiene sempre i dati che vengono inviati dopo la sua sottoscrizione, ovvero i valori inviati in precedenza non vengono ricevuti .
const mySubject = new Rx.Subject();

mySubject.next(1);

const subscription1 = mySubject.subscribe(x => {
  console.log('From subscription 1:', x);
});

mySubject.next(2);

const subscription2 = mySubject.subscribe(x => {
  console.log('From subscription 2:', x);
});

mySubject.next(3);

subscription1.unsubscribe();

mySubject.next(4);

Con questo esempio, ecco il risultato che verrà stampato nella console:

From subscription 1: 2
From subscription 1: 3
From subscription 2: 3
From subscription 2: 4

Nota come gli abbonamenti che arrivano in ritardo perdono alcuni dei dati che sono stati inseriti nell'argomento.

  1. Replay soggetti : può aiutare mantenendo un buffer dei valori precedenti che verranno emessi ai nuovi abbonamenti.

Ecco un esempio di utilizzo per gli argomenti di replay in cui buffer of 2 previous valuesvengono conservati ed emessi su nuovi abbonamenti:

const mySubject = new Rx.ReplaySubject(2);

mySubject.next(1);
mySubject.next(2);
mySubject.next(3);
mySubject.next(4);

mySubject.subscribe(x => {
  console.log('From 1st sub:', x);
});

mySubject.next(5);

mySubject.subscribe(x => {
  console.log('From 2nd sub:', x);
});

Ecco cosa ci offre alla console:

From 1st sub: 3
From 1st sub: 4
From 1st sub: 5
From 2nd sub: 4
From 2nd sub: 5
  1. Soggetti comportamentali : sono simili ai soggetti replay, ma riemetteranno solo l'ultimo valore emesso, o un valore predefinito se non è stato emesso alcun valore in precedenza:
const mySubject = new Rx.BehaviorSubject('Hey now!');

mySubject.subscribe(x => {
  console.log('From 1st sub:', x);
});

mySubject.next(5);

mySubject.subscribe(x => {
  console.log('From 2nd sub:', x);
});

E il risultato:

From 1st sub: Hey now!
From 1st sub: 5
From 2nd sub: 5

Riferimento: https://alligator.io/rxjs/subjects/


4

Da: libro di Randall Koutnik "Build Reactive Websites with RxJS". :

Un soggetto è un oggetto che è un osservabile turbocompresso. In sostanza, un Soggetto si comporta in modo molto simile a un normale osservabile, ma ogni abbonamento è collegato alla stessa fonte. I soggetti sono anche osservatori e dispongono di metodi next, error e done per inviare dati a tutti gli abbonati contemporaneamente. Poiché i soggetti sono osservatori, possono essere passati direttamente a un bando di iscrizione e tutti gli eventi dell'osservabile originale verranno inviati tramite l'oggetto ai suoi abbonati.

Possiamo usare ReplaySubject per tenere traccia della cronologia. Un ReplaySubject registra gli ultimi n eventi e li mostra a ogni nuovo iscritto. Ad esempio nell'applicazione di chat. Possiamo usarlo per tenere traccia del record della cronologia chat precedente.

Un BehaviorSubject è una versione semplificata di ReplaySubject . Il ReplaySubject ha memorizzato un numero arbitrario di eventi, il BehaviorSubject registra solo il valore dell'ultimo evento. Ogni volta che un BehaviorSubject registra una nuova sottoscrizione, emette il valore più recente per l'abbonato così come tutti i nuovi valori che vengono passati. BehaviorSubject è utile quando si tratta di singole unità di stato, come le opzioni di configurazione.


1

La risposta più votata è chiaramente sbagliata affermando che:

"Se si inizializza un ReplaySubjectcon una dimensione del buffer di 1, in realtà si comporta proprio come un BehaviorSubject"


Questo non è del tutto vero; controlla questo fantastico post sul blog sulle differenze tra questi due. Ad esempio, se ti iscrivi a un completato BehaviorSubject, non riceverai l'ultimo valore ma ReplaySubject(1)riceverai l'ultimo valore.

Questa è una differenza importante che non dovrebbe essere trascurata:

const behavior = new BehaviorSubject(null);
const replay = new ReplaySubject(1);

behavior.skip(1).subscribe(v => console.log('BehaviorSubject:', v));
replay.subscribe(v => console.log('ReplaySubject:', v));

behavior.next(1);
behavior.next(2);
behavior.complete();
behavior.subscribe(v => console.log('Late B subscriber:', v));

replay.next(1);
replay.next(2);
replay.complete();
replay.subscribe(v => console.log('Late R subscriber:', v));

Controlla questo esempio di codice qui che proviene da un altro fantastico post del blog sull'argomento.


0
     // ***********Subject  concept ***********
    let subject = new Subject<string>();


    subject.next("Eureka");
    subject.subscribe((data) => {
      console.log("Subscriber 1 got data >>>>> "+ data);
    });
    subject.subscribe((data) => {
      console.log("Subscriber 2 got data >>>>> "+ data);
    });

       // ********behaviour subject*********
    // Behavior subjects need a first value
let subject1 = new BehaviorSubject<string>("First value");


subject1.asObservable().subscribe((data) => {
  console.log("First subscriber got data behaviour subject>>>>> "+ data);
});
subject1.next("Second value")
  • Oggetto: un abbonato riceverà solo i valori pubblicati dopo la sottoscrizione.
  • BehaviorSubject: i nuovi abbonati ottengono l'ultimo valore pubblicato O il valore iniziale immediatamente dopo la sottoscrizione.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.