BehaviorSubject vs Observable?


690

Sto esaminando i modelli di Angular RxJs e non capisco la differenza tra a BehaviorSubjecte an Observable.

Secondo la mia comprensione, a BehaviorSubjectè un valore che può cambiare nel tempo (può essere sottoscritto e gli abbonati possono ricevere risultati aggiornati). Questo sembra essere esattamente lo stesso scopo di un Observable.

Quando useresti un Observablevs a BehaviorSubject? Ci sono vantaggi nell'usare un BehaviorSubjectover an Observableo viceversa?

Risposte:


970

BehaviorSubject è un tipo di soggetto, un soggetto è un tipo speciale di osservabile, quindi puoi iscriverti a messaggi come qualsiasi altro osservabile. Le caratteristiche uniche di BehaviorSubject sono:

  • Ha bisogno di un valore iniziale in quanto deve sempre restituire un valore in abbonamento anche se non ha ricevuto un next()
  • Al momento della sottoscrizione, restituisce l'ultimo valore dell'oggetto. Un osservabile regolare si innesca solo quando riceve unonnext
  • in qualsiasi momento, è possibile recuperare l'ultimo valore del soggetto in un codice non osservabile utilizzando il getValue()metodo

Le caratteristiche uniche di un soggetto rispetto a un osservabile sono:

  • È un osservatore oltre ad essere osservabile, quindi puoi anche inviare valori a un soggetto oltre a iscriverti.

Inoltre, puoi ottenere un osservabile dall'argomento del comportamento usando il asObservable()metodo on BehaviorSubject.

Osservabile è un generico ed BehaviorSubjectè tecnicamente un sottotipo di osservabile perché BehaviorSubject è un osservabile con qualità specifiche.

Esempio con BehaviorSubject :

// Behavior Subject

// a is an initial value. if there is a subscription 
// after this, it would get "a" value immediately
let bSubject = new BehaviorSubject("a"); 

bSubject.next("b");

bSubject.subscribe(value => {
  console.log("Subscription got", value); // Subscription got b, 
                                          // ^ This would not happen 
                                          // for a generic observable 
                                          // or generic subject by default
});

bSubject.next("c"); // Subscription got c
bSubject.next("d"); // Subscription got d

Esempio 2 con soggetto regolare:

// Regular Subject

let subject = new Subject(); 

subject.next("b");

subject.subscribe(value => {
  console.log("Subscription got", value); // Subscription wont get 
                                          // anything at this point
});

subject.next("c"); // Subscription got c
subject.next("d"); // Subscription got d

Un osservabile può essere creato da entrambi Subjecte BehaviorSubjectusando subject.asObservable().

L'unica differenza è che non puoi inviare valori a un next()metodo osservabile .

Nei servizi angolari, utilizzerei BehaviorSubjectper un servizio dati come un servizio angolare spesso inizializzato prima che il componente e il comportamento comportino che il componente che consuma il servizio riceva gli ultimi dati aggiornati anche se non ci sono nuovi aggiornamenti dalla sottoscrizione del componente a questi dati.


7
Sono un po 'confuso con l'esempio 2 di soggetto normale. Perché l'abbonamento non riceverà nulla di buono anche sulla seconda riga in cui invii valori a subject usando subject.next ("b")?
jmod999,

25
@ jmod999 Il secondo esempio è un soggetto regolare che riceve un valore subito prima della chiamata alla sottoscrizione. In soggetti normali, l'abbonamento viene attivato solo per i valori ricevuti dopo che è stato chiamato l'abbonamento. Poiché viene ricevuto subito prima dell'abbonamento, non viene inviato all'abbonamento.
Shantanu Bhadoria,

Una nota su quella fantastica soluzione, se la usi in una funzione e la restituisci, allora restituisci un'osservabile. Ho avuto alcuni problemi con la restituzione di un argomento, e confonde gli altri sviluppatori che sanno solo cosa sono gli osservabili
sam

8
Ho avuto un'intervista ad Angular 4 mercoledì. Dato che sto ancora imparando la nuova piattaforma, mi ha fatto inciampare chiedendomi qualcosa del tipo "Cosa succederà se mi iscrivo a un osservabile che si trova in un modulo che non è ancora stato caricato in modo pigro?" Non ne ero sicuro, ma mi disse che la risposta era usare un oggetto BS - ESATTAMENTE come l'onorevole Bhadoria lo ha spiegato sopra. La risposta è stata usare un oggetto BS perché restituisce sempre l'ultimo valore (almeno è così che ricordo il commento finale dell'intervistatore su quello).
bob.mazzo

1
@ bob.mazzo Perché devo usare un BSubject per quel caso? - Se mi iscrivo a quell'osservatore non riceverò nulla perché l'osservatore non è stato inizializzato, quindi non può inviare dati agli osservatori e se uso un oggetto BS non riceverò nulla per lo stesso motivo. In entrambi i casi, l'abbonato non riceverà nulla perché si trova all'interno di un modulo che non è stato inizializzato. Ho ragione?
Rafael Reyes,

183

Osservabile: risultato diverso per ciascun osservatore

Una differenza molto importante. Poiché Observable è solo una funzione, non ha alcuno stato, quindi per ogni nuovo osservatore, esegue il codice di creazione osservabile ancora e ancora. Questo risulta in:

Il codice viene eseguito per ciascun osservatore. Se è una chiamata HTTP, viene chiamata per ciascun osservatore

Ciò causa gravi bug e inefficienze

BehaviorSubject (o Subject) memorizza i dettagli dell'osservatore, esegue il codice una sola volta e dà il risultato a tutti gli osservatori.

Ex:

JSBin: http://jsbin.com/qowulet/edit?js,console

// --- Observable ---
let randomNumGenerator1 = Rx.Observable.create(observer => {
   observer.next(Math.random());
});

let observer1 = randomNumGenerator1
      .subscribe(num => console.log('observer 1: '+ num));

let observer2 = randomNumGenerator1
      .subscribe(num => console.log('observer 2: '+ num));


// ------ BehaviorSubject/ Subject

let randomNumGenerator2 = new Rx.BehaviorSubject(0);
randomNumGenerator2.next(Math.random());

let observer1Subject = randomNumGenerator2
      .subscribe(num=> console.log('observer subject 1: '+ num));
      
let observer2Subject = randomNumGenerator2
      .subscribe(num=> console.log('observer subject 2: '+ num));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.3/Rx.min.js"></script>

Produzione :

"observer 1: 0.7184075243594013"
"observer 2: 0.41271850211336103"
"observer subject 1: 0.8034263165479893"
"observer subject 2: 0.8034263165479893"

Osserva come l'utilizzo di Observable.createun output diverso sia stato creato per ciascun osservatore, ma abbia BehaviorSubjectfornito lo stesso output a tutti gli osservatori. Questo è importante.


Altre differenze riassunte.

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃         Observable                  ┃     BehaviorSubject/Subject         ┃      
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ 
┃ Is just a function, no state        ┃ Has state. Stores data in memory    ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃ Code run for each observer          ┃ Same code run                       ┃
┃                                     ┃ only once for all observers         ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃ Creates only Observable             ┃Can create and also listen Observable┃
┃ ( data producer alone )             ┃ ( data producer and consumer )      ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃ Usage: Simple Observable with only  ┃ Usage:                              ┃
┃ one Obeserver.                      ┃ * Store data and modify frequently  ┃
┃                                     ┃ * Multiple observers listen to data ┃
┃                                     ┃ * Proxy between Observable  and     ┃
┃                                     ┃   Observer                          ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

3
chiunque verrà da KnockoutJS's ko.observable()subito vedrà più parallelismi Rx.BehaviorSubjectrispetto aRx.Observable
Simon_Weaver,

@Skeptor Observable: il metodo iscriviti attiverà sempre il metodo onNext associato all'osservatore e porterà il valore restituito. Comportamento Oggetto / Oggetto: restituirà sempre l'ultimo valore nello stream. qui il metodo di sottoscrizione con l'oggetto non si innescherà il metodo Next del suo Observer fino a quando non trova l'ultimo valore nel flusso.
Mohan Ram,

62

Osservabile e soggetto entrambi sono mezzi osservabili che un osservatore può seguirli. ma entrambi hanno alcune caratteristiche uniche. Inoltre ci sono in totale 3 tipi di soggetti, ognuno dei quali ha di nuovo caratteristiche uniche. cerchiamo di capire ciascuno di essi.

puoi trovare l'esempio pratico qui su Stackblitz . (È necessario controllare la console per vedere l'output effettivo)

inserisci qui la descrizione dell'immagine

Observables

Sono freddi: il codice viene eseguito quando hanno almeno un singolo osservatore.

Crea copia dei dati: osservabile crea una copia dei dati per ciascun osservatore.

Uni-direzionale: Observer non può assegnare valore a osservabile (origine / master).

Subject

Sono caldi: il codice viene eseguito e il valore viene trasmesso anche se non è presente alcun osservatore.

Condivide i dati: gli stessi dati vengono condivisi tra tutti gli osservatori.

bidirezionale: l' osservatore può assegnare un valore osservabile (origine / master).

Se stai usando il soggetto, allora perdi tutti i valori trasmessi prima della creazione dell'osservatore. Quindi ecco che arriva Replay Subject

ReplaySubject

Sono caldi: il codice viene eseguito e il valore viene trasmesso anche se non è presente alcun osservatore.

Condivide i dati: gli stessi dati vengono condivisi tra tutti gli osservatori.

bidirezionale: l' osservatore può assegnare un valore osservabile (origine / master). più

Riproduci il flusso di messaggi: non importa quando ti iscrivi all'oggetto di riproduzione riceverai tutti i messaggi trasmessi.

Nell'argomento e nel soggetto di riproduzione non è possibile impostare il valore iniziale su osservabile. Quindi ecco che arriva il soggetto comportamentale

BehaviorSubject

Sono caldi: il codice viene eseguito e il valore viene trasmesso anche se non è presente alcun osservatore.

Condivide i dati: gli stessi dati vengono condivisi tra tutti gli osservatori.

bidirezionale: l' osservatore può assegnare un valore osservabile (origine / master). più

Riproduci il flusso di messaggi: non importa quando ti iscrivi all'oggetto di riproduzione riceverai tutti i messaggi trasmessi.

È possibile impostare il valore iniziale: è possibile inizializzare l'osservabile con il valore predefinito.


3
Vale la pena ricordare che a ReplaySubjectha una storia e può trasmettere / emettere una sequenza di (vecchi) valori. Solo quando il buffer è impostato su 1 si comporta in modo simile a BehaviorSubject.
Wilt,

28

L'oggetto Observable rappresenta una raccolta basata su push.

Le interfacce Observer e Observable forniscono un meccanismo generalizzato per la notifica basata su push, noto anche come modello di progettazione dell'osservatore. L'oggetto osservabile rappresenta l'oggetto che invia notifiche (il provider); l'oggetto Observer rappresenta la classe che li riceve (l'osservatore).

La classe Soggetto eredita sia Osservabile che Osservatore, nel senso che è sia un osservatore che un osservabile. È possibile utilizzare un soggetto per iscrivere tutti gli osservatori e quindi iscrivere l'oggetto a un'origine dati back-end

var subject = new Rx.Subject();

var subscription = subject.subscribe(
    function (x) { console.log('onNext: ' + x); },
    function (e) { console.log('onError: ' + e.message); },
    function () { console.log('onCompleted'); });

subject.onNext(1);
// => onNext: 1

subject.onNext(2);
// => onNext: 2

subject.onCompleted();
// => onCompleted

subscription.dispose();

Maggiori informazioni su https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/subjects.md


qual è la differenza tra membership.dispose () e subscription.unsubscribe ()?
Choopage - Jek Bao

4
@choopage nessuna differenza. quest'ultimo è il nuovo modo
Royi Namir,

Se si desidera annullare l'iscrizione prima che l'oggetto venga eliminato, in caso contrario, l'abbonamento diventa un rifiuto poiché si iscrive a un valore null.
Sophie Zhang,

20

Una cosa che non vedo negli esempi è che quando si lancia BehaviorSubject su Observable tramite asObservable, eredita il comportamento di restituzione dell'ultimo valore in abbonamento.

È un po 'complicato, poiché spesso le biblioteche espongono i campi come osservabili (cioè i parametri in ActivatedRoute in Angular2), ma possono usare Soggetto o Oggetto dietro le quinte. Quello che usano influenzerebbe il comportamento dell'abbonamento.

Vedi qui http://jsbin.com/ziquxapubo/edit?html,js,console

let A = new Rx.Subject();
let B = new Rx.BehaviorSubject(0);

A.next(1);
B.next(1);

A.asObservable().subscribe(n => console.log('A', n));
B.asObservable().subscribe(n => console.log('B', n));

A.next(2);
B.next(2);

11

Un osservabile ti consente di iscriverti solo mentre un soggetto ti consente sia di pubblicare che di iscriverti.

Quindi un soggetto consente ai tuoi servizi di essere utilizzati sia come editore che come abbonato.

A partire da ora, non sono così bravo, Observablequindi condividerò solo un esempio di Subject.

Comprendiamo meglio con un esempio di CLI angolare . Esegui i comandi seguenti:

npm install -g @angular/cli

ng new angular2-subject

cd angular2-subject

ng serve

Sostituisci il contenuto di app.component.htmlcon:

<div *ngIf="message">
  {{message}}
</div>

<app-home>
</app-home>

Esegui il comando ng g c components/homeper generare il componente home. Sostituisci il contenuto di home.component.htmlcon:

<input type="text" placeholder="Enter message" #message>
<button type="button" (click)="setMessage(message)" >Send message</button>

#messageè la variabile locale qui. Aggiungi una proprietà message: string; alla app.component.tsclasse di.

Esegui questo comando ng g s service/message. Questo genererà un servizio a src\app\service\message.service.ts. Fornisci questo servizio all'app .

Importa Subjectin MessageService. Aggiungi anche un argomento. Il codice finale dovrebbe apparire così:

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class MessageService {

  public message = new Subject<string>();

  setMessage(value: string) {
    this.message.next(value); //it is publishing this value to all the subscribers that have already subscribed to this message
  }
}

Ora, iniettare questo servizio home.component.tse passarne un'istanza al costruttore. Fallo app.component.tsanche per questo . Utilizzare questa istanza del servizio per passare il valore #messagealla funzione di servizio setMessage:

import { Component } from '@angular/core';
import { MessageService } from '../../service/message.service';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent {

  constructor(public messageService:MessageService) { }

  setMessage(event) {
    console.log(event.value);
    this.messageService.setMessage(event.value);
  }
}

All'interno app.component.ts, iscriviti e annulla l'iscrizione (per evitare perdite di memoria) a Subject:

import { Component, OnDestroy } from '@angular/core';
import { MessageService } from './service/message.service';
import { Subscription } from 'rxjs/Subscription';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {

  message: string;
  subscription: Subscription;

  constructor(public messageService: MessageService) { }

  ngOnInit() {
    this.subscription = this.messageService.message.subscribe(
      (message) => {
        this.message = message;
      }
    );
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

Questo è tutto.

Ora, qualsiasi valore inserito all'interno #messagedi home.component.htmldeve essere stampato {{message}}all'internoapp.component.html


Perché l'immagine gigante? Se non è direttamente correlato alla tua risposta, sembra come un voto.
ruffin,

@ruffin Questa è solo una risposta media con un numero medio di voti, guarda il mio profilo. Non sicuramente esca: D
Mohammed Zameer,

1
Ti ho dato un voto prima, ma hai evitato la domanda sul perché l'immagine sia lì. Non è direttamente correlato alla tua risposta. Non importa se hai molti rappresentanti o meno - se l'immagine non è direttamente e specificamente esplicativa, ti chiedo di rimuoverla . / scrollata di spalle
ruffin,

1
@ruffin Se va contro il consenso della comunità, allora non dovrebbe esserci sicuramente!
Mohammed Zameer,

4

app.component.ts

behaviourService.setName("behaviour");

behaviour.service.ts

private name = new BehaviorSubject("");
getName = this.name.asObservable();`

constructor() {}

setName(data) {
    this.name.next(data);
}

custom.component.ts

behaviourService.subscribe(response=>{
    console.log(response);    //output: behaviour
});

1

BehaviorSubject vs Observable : RxJS ha osservatori e osservabili, Rxjs offre più classi da utilizzare con flussi di dati e uno di questi è un BehaviorSubject.

Osservabili : gli osservabili sono raccolte pigre di più valori nel tempo.

BehaviorSubject : un oggetto che richiede un valore iniziale ed emette il suo valore corrente ai nuovi abbonati.

 // RxJS v6+
import { BehaviorSubject } from 'rxjs';

const subject = new BehaviorSubject(123);

//two new subscribers will get initial value => output: 123, 123
subject.subscribe(console.log);
subject.subscribe(console.log);

//two subscribers will get new value => output: 456, 456
subject.next(456);

//new subscriber will get latest value (456) => output: 456
subject.subscribe(console.log);

//all three subscribers will get new value => output: 789, 789, 789
subject.next(789);

// output: 123, 123, 456, 456, 456, 789, 789, 789

1

Pensa agli osservabili come a un tubo con dentro l'acqua che scorre, a volte scorre acqua ea volte no. In alcuni casi, potresti effettivamente aver bisogno di una pipa che contiene sempre acqua, puoi farlo creando una pipa speciale che contenga sempre un'acqua, non importa quanto sia piccola, chiamiamo questa pipa speciale BehaviorSubject , se ti capita di essere un fornitore di approvvigionamento idrico nella tua comunità, puoi dormire sonni tranquilli di notte sapendo che il tuo tubo appena installato funziona.

In termini tecnici: potresti riscontrare casi in cui un Osservabile dovrebbe sempre avere valore in esso, forse vuoi catturare il valore di un testo di input nel tempo, puoi quindi creare un'istanza di BehaviorSubject per garantire questo tipo di comportamento, diciamo:


const firstNameChanges = new BehaviorSubject("<empty>");

// pass value changes.
firstNameChanges.next("Jon");
firstNameChanges.next("Arya");

È quindi possibile utilizzare "valore" per campionare le modifiche nel tempo.


firstNameChanges.value;

Ciò risulta utile quando si combinano gli osservabili in un secondo momento, dando un'occhiata al tipo di flusso come BehaviorSubject, quindi è possibile assicurarsi che il flusso si attivi o segnali almeno una volta almeno .

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.