javascript - Vad är skillnaden mellan BehaviorSubject och Observable?


Jag undersöker designmönstren i RxJS, och jag förstår inte skillnaden mellan BehaviorSubject och Observable.

Enligt min förståelse kan BehaviorSubject innehålla ett värde som kan ändras. Det kan abonneras på och abonnenter kan ta emot uppdaterade värden. Båda verkar ha exakt samma syfte.

  1. När bör man använda Observable vs BehaviorSubject, och vice versa?
  2. Vilka är fördelarna med att använda BehaviorSubject jämfört med Observable, och vice versa?

Svar #1

BehaviorSubject är en variant av Subject, en typ av Observable som man kan "prenumerera på" precis som vilket annat Observable som helst.

Egenskaper hos BehaviorSubject

  • Det behöver ett initialvärde eftersom det alltid måste returnera ett värde när man prenumererar, även om det inte har fått metoden next()
  • Vid prenumeration returnerar det det senaste värdet av Subject. Ett vanligt Observable triggas endast när det mottager metoden onNext()
  • När som helst kan man hämta det senaste värdet av Subject i en icke-Observable med metoden getValue()

Egenskaper hos ämnet

  • Ett Subject är en "observatör" utöver att vara ett Observable; därför kan man också skicka värden till ett Subject samtidigt som man prenumererar på det
  • Man kan få ett värde från BehaviorSubject med metoden asObservable()

Exempel 1 med BehaviorSubject

// BehaviorSubject.
// 'A' is an initial value. If there is a Subscription 
// after it, it would immediately get the value 'A'.

beSubject = new BehaviorSubject('a'); 

beSubject.next('b');

beSubject.subscribe(value => {
  console.log('Subscription received the value ', value);

  // Subscription received B. It would not happen
  // for an Observable or Subject by default.
});

beSubject.next('c');
// Subscription received C.

beSubject.next('d');
// Subscription received D.

Exempel 2 med ämne

// Subject.

subject = new Subject(); 

subject.next('b');

subject.subscribe(value => {
  console.log('Subscription received the value ', value);

  // Subscription won't receive anything at this point.
});

subject.next('c');
// Subscription received C.

subject.next('d');
// Subscription received D.

Ett Observable kan skapas både från Subject och BehaviorSubject; till exempel subjectName.asObservable().

Den enda skillnaden är att man inte kan skicka värden till ett Observable med metoden next().

I Angular rekommenderas det att använda BehaviorSubject för att överföra data eftersom en tjänst ofta initieras innan en komponent.

BehaviorSubject säkerställer att komponenten som använder tjänsten får den senaste uppdaterade datan, även om det inte finns några nya uppdateringar, på grund av komponentens prenumeration på tjänsten.

Svar #2

Observerbar: Olika resultat för varje observatör

En mycket, mycket viktig skillnad. Eftersom Observable bara är en funktion har den inget tillstånd, så för varje ny Observer kör den koden för att skapa observable om och om igen. Detta resulterar i:

Koden körs för varje observatör. Om det är ett HTTP-anrop, anropas det för varje observatör.

Detta orsakar stora buggar och ineffektivitet

BehaviorSubject (eller Subject) lagrar information om observatörer, kör koden bara en gång och ger resultatet till alla observatörer.

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>

Utdata :

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

Observera hur användning av Observable.create skapade olika resultat för varje observatör, men BehaviorSubject gav samma resultat för alla observatörer. Detta är viktigt.

Andra skillnader sammanfattade.

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃         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                          ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

Svar #3

Observable och Subject är båda observables, vilket betyder att en observatör kan följa dem. Båda har vissa unika egenskaper. Det finns 3 typer av Subjects, som också var och en har unika egenskaper.

Du kan hitta det praktiska exemplet här på stackblitz. (Du behöver kolla konsolen för att se det faktiska resultatet)

Observables

De är kalla: Kod körs när de har minst en observatör.

Skapar kopia av data: Observable skapar en kopia av data för varje observatör.

Enkelriktad: Observatören kan inte tilldela värde till det observerbara (ursprung/master).

Subject

De är heta: kod körs och värde sänds ut även om det inte finns någon observatör.

Aktiedata: Samma data delas mellan alla observatörer.

bidirektionell: Observatören kan tilldela värde till det observerbara (origin/master).

Om du använder subject missar du alla värden som sänds ut innan observatören skapas. Så här kommer Replay Subject

ReplaySubject

De är heta: kod körs och värde sänds ut även om det inte finns någon observatör.

Aktiedata: Samma data delas mellan alla observatörer.

tvåvägs: Observatören kan tilldela värde till observerbar (origin/master). plus

Spela upp meddelandeströmmen: Oavsett när du prenumererar på återuppspelningsämnet kommer du att ta emot alla sända meddelanden.

I Subject och ReplaySubject kan du inte sätta det initiala värdet till observable. Så här kommer BehavioralSubject...

BehaviorSubject

De är heta: kod körs och värde sänds ut även om det inte finns någon observatör.

Aktiedata: Samma data delas mellan alla observatörer.

tvåvägs: Observatören kan tilldela värde till observerbart (origin/master). plus

Du kan ställa in ett initialvärde: Du kan initiera det observerbara med ett standardvärde.

Svar #4

Observable-objektet representerar en tryckbaserad samling.

Gränssnitten Observer och Observable tillhandahåller en generell mekanism för push-baserad notifiering, även känd som observatörsdesignmönstret. Det Observable-objektet representerar objektet som skickar notifieringar (leverantören); Observer-objektet representerar klassen som tar emot dem (observatören).

Subject-klassen ärver både Observable och Observer, i den meningen att den både är en observatör och ett observerbart objekt. Du kan använda ett subject för att prenumerera alla observatörer, och sedan prenumerera subject på en backend-datakälla

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

Mer på https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/subjects.md

Svar #5

En sak jag inte ser i exempel är att när du kastar BehaviorSubject till Observable via asObservable, ärver det beteendet att returnera senaste värdet vid prenumeration.

Det är den knepiga delen, eftersom bibliotek ofta exponerar fält som observerbara (d.v.s. parametrar i ActivatedRoute i Angular2), men kan använda Subject eller BehaviorSubject bakom kulisserna. Vad de använder skulle påverka beteendet vid prenumeration.

Se här 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);

Svar #6

Ett observerbart tillåter dig att endast prenumerera medan ett ämne tillåter dig både att publicera och prenumerera.

Så ett ämne tillåter dina tjänster att användas både som utgivare och som prenumerant.

Just nu är jag inte så bra på Observable, så jag kommer bara att dela ett exempel på Subject.

Låt oss förstå bättre med ett exempel på Angular CLI. Kör följande kommandon:

npm install -g @angular/cli

ng new angular2-subject

cd angular2-subject

ng serve

Byt ut innehållet i app.component.html med:

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

<app-home>
</app-home>

Kör kommandot ng g c components/home för att generera home-komponenten. Ersätt innehållet i home.component.html med:

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

#message är den lokala variabeln här. Lägg till en egenskap message: string; i app.component.ts-klassens klass.

Kör detta kommando ng g s service/message. Detta kommer att generera en tjänst på src\app\service\message.service.ts. Tillhandahåll denna tjänst till appen.

Importera Subject till MessageService. Lägg också till ett ämne. Den slutliga koden ska se ut så här:

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

Nu, injicera denna tjänst i home.component.ts och skicka en instans av den till konstruktorn. Gör detta även för app.component.ts. Använd denna tjänstinstans för att skicka värdet av #message till tjänstefunktionen 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);
  }
}

Inuti app.component.ts, prenumerera och avprenumerera (för att förhindra minnesläckor) på 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();
  }
}

Det är det.

Nu kommer alla värden som skrivs in i #message i home.component.html att skrivas ut i {{message}} i app.component.html

Svar #7

Tänk på Observables som ett rör med rinnande vatten i, ibland rinner vattnet och ibland gör det inte det. I vissa fall kan du faktiskt behöva ett rör som alltid har vatten i sig, du kan göra detta genom att skapa ett speciellt rör som alltid innehåller vatten oavsett hur lite det är, låt oss kalla detta speciella rör BehaviorSubject. Om du råkar vara vattenleverantör i ditt samhälle, kan du sova lugnt på natten och veta att ditt nyinstallerade rör bara fungerar.

I tekniska termer: du kan stöta på användningsfall där en Observable alltid bör ha ett värde i sig, kanske vill du fånga värdet av en inmatningstext över tid, du kan då skapa en instans av BehaviorSubject för att säkerställa denna typ av beteende, låt oss säga:

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

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

Du kan sedan använda "värde" för att ta prover på förändringar över tid.

firstNameChanges.value;

Detta är praktiskt när du kombinerar Observables senare, genom att titta på typen av din ström som BehaviorSubject kan du då säkerställa att strömmen åtminstone triggar eller signalerar bara en gång åtminstone.

Svar #8

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

Relaterade artiklar