BehaviorSubject vs Observable?

690

Ich untersuche Angular RxJs Muster und verstehe den Unterschied zwischen a BehaviorSubjectund an nicht Observable.

Nach meinem Verständnis ist a BehaviorSubjectein Wert, der sich im Laufe der Zeit ändern kann (kann abonniert werden und Abonnenten können aktualisierte Ergebnisse erhalten). Dies scheint genau der gleiche Zweck eines zu sein Observable.

Wann würden Sie ein Observablevs a verwenden BehaviorSubject? Gibt es Vorteile bei der Verwendung eines BehaviorSubjectüber Observableoder umgekehrt?

Kevin Mark
quelle

Antworten:

968

BehaviorSubject ist eine Art von Betreff, ein Betreff ist eine spezielle Art von Observable, sodass Sie Nachrichten wie jede andere Observable abonnieren können. Die einzigartigen Funktionen von BehaviorSubject sind:

  • Es benötigt einen Anfangswert, da es im Abonnement immer einen Wert zurückgeben muss, auch wenn es keinen erhalten hat next()
  • Beim Abonnement wird der letzte Wert des Betreffs zurückgegeben. Ein reguläres Observable wird nur ausgelöst, wenn es ein empfängtonnext
  • Mit der getValue()Methode können Sie jederzeit den letzten Wert des Betreffs in einem nicht beobachtbaren Code abrufen .

Einzigartige Merkmale eines Subjekts im Vergleich zu einem beobachtbaren Objekt sind:

  • Es ist nicht nur ein Beobachter, sondern auch ein Beobachter, sodass Sie neben dem Abonnieren auch Werte an ein Thema senden können.

Darüber hinaus können Sie mit der asObservable()Methode on ein beobachtbares Verhaltensthema abrufen BehaviorSubject.

Observable ist ein Generic und BehaviorSubjecttechnisch gesehen ein Untertyp von Observable, da BehaviorSubject ein Observable mit bestimmten Eigenschaften ist.

Beispiel mit 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

Beispiel 2 mit regulärem Thema:

// 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

Ein Observable kann aus beiden Subjectund BehaviorSubjectmit erstellt werden subject.asObservable().

Der einzige Unterschied besteht darin, dass Sie mit der next()Methode keine Werte an ein Observable senden können .

In Angular-Diensten würde ich BehaviorSubjectfür einen Datendienst verwenden, da ein Angular- Dienst häufig initialisiert wird, bevor Komponente und Verhaltenssubjekt sicherstellen, dass die Komponente, die den Dienst verbraucht, die zuletzt aktualisierten Daten erhält, auch wenn seit dem Abonnement dieser Daten durch die Komponente keine neuen Aktualisierungen vorliegen.

Shantanu Bhadoria
quelle
7
Ich bin ein bisschen verwirrt mit Beispiel 2 des regulären Fachs. Warum erhält das Abonnement auch in der zweiten Zeile, in der Sie mit subject.next ("b") Werte an subject senden, nichts?
jmod999
25
@ jmod999 Das zweite Beispiel ist ein regulärer Betreff, der unmittelbar vor dem Aufruf des Abonnements einen Wert erhält. In regulären Fächern wird das Abonnement nur für Werte ausgelöst, die nach dem Aufruf des Abonnements empfangen wurden. Da a direkt vor dem Abonnement empfangen wird, wird es nicht an das Abonnement gesendet.
Shantanu Bhadoria
Ein Hinweis zu dieser fantastischen Lösung: Wenn Sie diese in einer Funktion verwenden und zurückgeben, geben Sie eine beobachtbare Lösung zurück. Ich hatte einige Probleme mit der Rückgabe eines Themas und es verwirrt die anderen Entwickler, die nur wissen, was Observables sind
Sam
8
Ich hatte am Mittwoch ein Angular 4-Interview. Da ich noch die neue Plattform lerne, stolperte er über mich und fragte mich: "Was passiert, wenn ich ein Observable abonniere, das sich in einem Modul befindet, das noch nicht faul geladen wurde?" Ich war mir nicht sicher, aber er sagte mir, dass die Antwort darin bestand, ein BSubject zu verwenden - genau so, wie es Herr Bhadoria oben erklärt hat. Die Antwort war, ein BSubject zu verwenden, da es immer den neuesten Wert zurückgibt (zumindest erinnere ich mich so an den letzten Kommentar des Interviewers dazu).
Bob.mazzo
1
@ bob.mazzo Warum muss ich für diesen Fall ein BSubject verwenden? - Wenn ich diesen Beobachter abonniere, erhalte ich nichts, da der Beobachter nicht initialisiert wurde, sodass er keine Daten an Beobachter senden kann. Wenn ich ein BSubject verwende, erhalte ich aus demselben Grund auch nichts. In beiden Fällen erhält der Teilnehmer nichts, da er sich in einem Modul befindet, das nicht initialisiert wurde. Habe ich recht?
Rafael Reyes
183

Beobachtbar: Unterschiedliches Ergebnis für jeden Beobachter

Ein sehr sehr wichtiger Unterschied. Da Observable nur eine Funktion ist, hat es keinen Status. Daher führt es für jeden neuen Observer den beobachtbaren Erstellungscode immer wieder aus. Das führt zu:

Der Code wird für jeden Beobachter ausgeführt. Wenn es sich um einen HTTP-Aufruf handelt, wird er für jeden Beobachter aufgerufen

Dies führt zu schwerwiegenden Fehlern und Ineffizienzen

BehaviorSubject (oder Subject) speichert Beobachterdetails, führt den Code nur einmal aus und gibt das Ergebnis an alle Beobachter weiter.

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>

Ausgabe :

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

Beobachten Sie, wie die Verwendung Observable.createfür jeden Beobachter unterschiedliche Ausgaben erzeugt hat, aber BehaviorSubjectfür alle Beobachter die gleiche Ausgabe ergab. Das ist wichtig.


Andere Unterschiede zusammengefasst.

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃         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                          ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
Vamshi
quelle
3
Jeder, der von kommt, KnockoutJS's ko.observable()wird sofort mehr Parallelen zu sehen Rx.BehaviorSubjectalsRx.Observable
Simon_Weaver
@Skeptor Observable: Die Methode subscribe löst immer die dem Beobachter zugeordnete Methode onNext aus und bringt den Rückgabewert. BehaviourSubject / Subject: Gibt immer den neuesten Wert im Stream zurück. Hier löst die Subcribe-Methode mit dem Betreff die onNext-Methode ihres Observer erst aus, wenn der neueste Wert im Stream gefunden wurde.
Mohan Ram
62

Beobachtbar und Subjekt sind beide beobachtbar, dh ein Beobachter kann sie verfolgen. aber beide haben einige einzigartige Eigenschaften. Ferner gibt es insgesamt 3 Arten von Themen, von denen jedes wieder einzigartige Eigenschaften aufweist. Lassen Sie uns versuchen, jeden von ihnen zu verstehen.

Das praktische Beispiel finden Sie hier auf stackblitz . (Sie müssen die Konsole überprüfen, um die tatsächliche Ausgabe zu sehen.)

Geben Sie hier die Bildbeschreibung ein

Observables

Sie sind kalt: Code wird ausgeführt, wenn sie mindestens einen einzigen Beobachter haben.

Erstellt eine Kopie der Daten: Observable erstellt eine Kopie der Daten für jeden Beobachter.

Unidirektional: Der Beobachter kann dem beobachtbaren Wert (Ursprung / Master) keinen Wert zuweisen.

Subject

Sie sind heiß: Code wird ausgeführt und der Wert wird gesendet, auch wenn kein Beobachter vorhanden ist.

Daten teilen : Dieselben Daten werden von allen Beobachtern geteilt.

bidirektional: Der Beobachter kann dem beobachtbaren Wert (Ursprung / Master) einen Wert zuweisen.

Wenn Sie den Betreff verwenden, verpassen Sie alle Werte, die vor der Erstellung des Beobachters gesendet werden. Hier kommt also das Thema "Wiedergabe"

ReplaySubject

Sie sind heiß: Code wird ausgeführt und der Wert wird gesendet, auch wenn kein Beobachter vorhanden ist.

Daten teilen : Dieselben Daten werden von allen Beobachtern geteilt.

bidirektional: Der Beobachter kann dem beobachtbaren Wert (Ursprung / Master) einen Wert zuweisen. Plus

Nachrichtenstrom erneut abspielen: Unabhängig davon, wann Sie den Betreff für die Wiedergabe abonnieren, erhalten Sie alle gesendeten Nachrichten.

In Betreff und Betreff wiedergeben können Sie den Anfangswert nicht auf beobachtbar setzen. Hier kommt also das Verhaltensthema

BehaviorSubject

Sie sind heiß: Code wird ausgeführt und der Wert wird gesendet, auch wenn kein Beobachter vorhanden ist.

Daten teilen : Dieselben Daten werden von allen Beobachtern geteilt.

bidirektional: Der Beobachter kann dem beobachtbaren Wert (Ursprung / Master) einen Wert zuweisen. Plus

Nachrichtenstrom erneut abspielen: Unabhängig davon, wann Sie den Betreff für die Wiedergabe abonnieren, erhalten Sie alle gesendeten Nachrichten.

Sie können den Anfangswert festlegen: Sie können das Observable mit dem Standardwert initialisieren.

Kedar9444
quelle
3
Könnte erwähnenswert sein, dass a ReplaySubjecteine Historie hat und eine Folge von (alten) Werten senden / senden kann. Nur wenn der Puffer auf 1 gesetzt ist, verhält er sich ähnlich wie a BehaviorSubject.
Wilt
28

Das Observable-Objekt repräsentiert eine Push-basierte Sammlung.

Die Schnittstellen Observer und Observable bieten einen allgemeinen Mechanismus für die Push-basierte Benachrichtigung, der auch als Observer Design Pattern bezeichnet wird. Das Observable-Objekt stellt das Objekt dar, das Benachrichtigungen sendet (den Anbieter). Das Observer-Objekt repräsentiert die Klasse, die sie empfängt (den Observer).

Die Subject-Klasse erbt sowohl Observable als auch Observer in dem Sinne, dass sie sowohl Beobachter als auch Observable ist. Sie können einen Betreff verwenden, um alle Beobachter zu abonnieren, und dann den Betreff einer Backend-Datenquelle abonnieren

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();

Weitere Informationen finden Sie unter https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/subjects.md

Md Ayub Ali Sarker
quelle
Was ist der Unterschied zwischen Abonnement.dispose () und Abonnement.unsubscribe ()?
choopage - Jek Bao
4
@choopage kein Unterschied. Letzteres ist der neue Weg
Royi Namir
Sollte sich das Abonnement abmelden, bevor der Betreff entsorgt wird, wird das Abonnement zu einem Müll, da es einen Nullwert abonniert.
Sophie Zhang
20

Eine Sache, die ich in Beispielen nicht sehe, ist, dass wenn Sie BehaviorSubject über asObservable in Observable umwandeln, das Verhalten der Rückgabe des letzten Werts beim Abonnement geerbt wird.

Es ist das Knifflige, da Bibliotheken Felder häufig als beobachtbar verfügbar machen (dh Parameter in ActivatedRoute in Angular2), aber möglicherweise Subject oder BehaviorSubject hinter den Kulissen verwenden. Was sie verwenden, würde das Abonnementverhalten beeinflussen.

Siehe hier 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);
Lukasz Marek Sielski
quelle
11

Mit einem Observable können Sie nur abonnieren, während Sie mit einem Betreff sowohl veröffentlichen als auch abonnieren können.

Ein Betreff ermöglicht es also, Ihre Dienste sowohl als Herausgeber als auch als Abonnent zu verwenden.

Ab sofort bin ich nicht so gut darin, Observabledaher werde ich nur ein Beispiel dafür nennen Subject.

Lassen Sie uns anhand eines Angular CLI- Beispiels besser verstehen . Führen Sie die folgenden Befehle aus:

npm install -g @angular/cli

ng new angular2-subject

cd angular2-subject

ng serve

Ersetzen Sie den Inhalt von app.component.htmldurch:

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

<app-home>
</app-home>

Führen Sie den Befehl aus ng g c components/home, um die Home-Komponente zu generieren. Ersetzen Sie den Inhalt von home.component.htmldurch:

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

#messageist hier die lokale Variable. Fügen Sie message: string; der app.component.tsKlasse von ' eine Eigenschaft hinzu .

Führen Sie diesen Befehl aus ng g s service/message. Dadurch wird ein Dienst bei generiert src\app\service\message.service.ts. Stellen Sie diesen Service für die App bereit .

Importieren Subjectin MessageService. Fügen Sie auch ein Thema hinzu. Der endgültige Code soll folgendermaßen aussehen:

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
  }
}

Fügen Sie diesen Service nun ein home.component.tsund übergeben Sie eine Instanz davon an den Konstruktor. Tun Sie dies auch für app.component.ts. Verwenden Sie diese Dienstinstanz, um den Wert von #messagean die Dienstfunktion zu übergeben 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);
  }
}

Im Inneren app.component.tskönnen Sie Folgendes abonnieren und abbestellen (um Speicherverluste zu vermeiden) 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();
  }
}

Das ist es.

Nun trat jeder beliebige Wert innerhalb #messagevon home.component.htmlwird zu bedruckenden {{message}}innenapp.component.html

Xameeramir
quelle
Warum das Riesenbild? Wenn es nicht direkt mit Ihrer Antwort zusammenhängt, scheint es wie ein Votebait.
Ruffin
@ruffin Dies ist nur eine durchschnittliche Antwort mit einer durchschnittlichen Anzahl von Stimmen. Schauen Sie sich mein Profil an. Nicht definitiv Votebait: D
Xameeramir
1
Ich habe Ihnen vorhin eine Gegenstimme gegeben, aber Sie sind der Frage ausgewichen, warum das Bild dort ist. Es hängt nicht direkt mit Ihrer Antwort zusammen. Es spielt keine Rolle, ob Sie viele Wiederholungen haben oder nicht - wenn das Bild nicht direkt und spezifisch verständlich ist, würde ich Sie bitten, es zu entfernen . / Achselzucken
Ruffin
1
@ruffin Wenn es gegen die Zustimmung der Community verstößt, sollte es sicher nicht da sein!
Xameeramir
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
});
Chandru Dev
quelle
1

BehaviorSubject vs Observable : RxJS verfügt über Beobachter und Observables, Rxjs bietet mehrere Klassen zur Verwendung mit Datenströmen an, und eine davon ist ein BehaviorSubject.

Observables : Observables sind verzögerte Sammlungen mehrerer Werte im Laufe der Zeit.

BehaviorSubject : Ein Betreff, der einen Anfangswert benötigt und seinen aktuellen Wert an neue Abonnenten ausgibt.

 // 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
Blitz
quelle
1

Stellen Sie sich Observables als ein Rohr mit fließendem Wasser vor, manchmal fließt Wasser und manchmal nicht. In einigen Fällen benötigen Sie möglicherweise tatsächlich ein Rohr, in dem sich immer Wasser befindet. Sie können dies tun, indem Sie ein spezielles Rohr erstellen, das immer ein Wasser enthält, egal wie klein es ist. Rufen Sie dieses spezielle Rohr BehaviorSubject auf , wenn Sie es gerade sind Als Wasserversorger in Ihrer Gemeinde können Sie nachts ruhig schlafen und wissen, dass Ihr neu installiertes Rohr einfach funktioniert.

In technischer Hinsicht: Es kann vorkommen, dass Anwendungsfälle auftreten, in denen ein Observable immer einen Wert enthalten sollte. Vielleicht möchten Sie den Wert eines Eingabetextes im Laufe der Zeit erfassen. Anschließend können Sie eine Instanz von BehaviorSubject erstellen, um diese Art von Verhalten sicherzustellen.


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

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

Sie können dann "Wert" verwenden, um Änderungen im Laufe der Zeit abzutasten.


firstNameChanges.value;

Dies ist praktisch, wenn Sie Observables später kombinieren. Wenn Sie sich den Typ Ihres Streams als BehaviorSubject ansehen, können Sie sicherstellen, dass der Stream mindestens einmal ausgelöst oder signalisiert wird .

Ronnel Reposo
quelle