BehaviorSubject vs Observable?

Patrzę na kątowe wzorce RxJs i nie rozumiem różnicy między BehaviorSubject a Observable.

Z mojego zrozumienia, BehaviorSubject jest wartość, która może zmieniać się w czasie(można subskrybować i subskrybenci mogą otrzymywać aktualizowane wyniki). Wydaje się, że jest to dokładnie ten sam cel Observable.

Kiedy użyłbyś Observable vs A BehaviorSubject? Czy są korzyści z używania BehaviorSubject nad Observable lub odwrotnie?

Author: Sunil Garg, 2016-09-14

5 answers

BehaviorSubject jest rodzajem podmiotu, podmiot jest specjalnym typem obserwowalnym, więc możesz subskrybować wiadomości jak każdy inny obserwowalny. Unikalne cechy BehaviorSubject to:

  • wymaga wartości początkowej, ponieważ zawsze musi zwracać wartość w subskrypcji, nawet jeśli nie otrzymał next()
  • po subskrypcji zwraca ostatnią wartość przedmiotu. Regularna obserwowalna wyzwala się tylko wtedy, gdy otrzymuje onnext
  • w dowolnym momencie, ostatnią wartość obiektu można pobrać w niezauważalnym kodzie za pomocą metody getValue().

Unikalne cechy podmiotu w porównaniu do obserwowalnego to:

  • jest obserwatorem oprócz bycia obserwowalnym, więc możesz również wysyłać wartości do tematu oprócz subskrybowania go.

Dodatkowo można uzyskać obserwowalny obiekt z behawioralnego obiektu za pomocą metody asObservable() na BehaviorSubject.

Obserwowalne to Rodzajnik, a BehaviorSubject jest technicznie podtypem obserwowalnym, ponieważ BehaviorSubject jest obserwowalnym o określonych cechach.

Przykład z 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

Przykład 2 z tematem regularnym:

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

Obserwowalny może być utworzony zarówno z Subject, jak i BehaviorSubject za pomocą subject.asObservable().

Jedyną różnicą jest to, że nie można wysyłać wartości do obserwowalnej metody next().

W usłudze Angular, użyłbym BehaviorSubject dla usługi data jako usługi angular często inicjalizuje się, zanim komponent i obiekt zachowania zapewni, że komponent korzystający z usługi otrzyma ostatnio zaktualizowane dane, nawet jeśli nie ma nowych aktualizacji od czasu subskrypcji komponentu na te dane.

 601
Author: Shantanu Bhadoria,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-01 07:39:06

Obserwowalny: inny wynik dla każdego obserwatora

Jedna, bardzo ważna różnica. Ponieważ obserwowalna jest tylko funkcją, nie ma żadnego stanu, więc dla każdego nowego obserwatora, wykonuje obserwowalny kod create raz po raz. To daje:

Kod jest uruchamiany dla każdego obserwatora . Jeśli jest to wywołanie HTTP, zostanie wywołane dla każdego obserwatora

To powoduje poważne błędy i nieefektywność

BehaviorSubject (lub Subject ) przechowuje obserwatora szczegóły, uruchamia kod tylko raz i daje wynik wszystkim obserwatorom .

Ex:

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

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

Wyjście:

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

Obserwuj, jak używając Observable.create stworzyliśmy różne wyjście dla każdego obserwatora, ale BehaviorSubject dało to samo wyjście dla wszystkich obserwatorów. To ważne.


Inne różnice podsumowane.

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃         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                          │
└─────────────────────────────────────┴─────────────────────────────────────┘
 93
Author: Skeptor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-04 08:41:52

Obserwowalny obiekt reprezentuje zbiór oparty na push.

[[1]}interfejsy obserwatora i obserwowalne zapewniają uogólniony mechanizm powiadomień push, znany również jako obserwator design pattern. Obiekt obserwowalny reprezentuje obiekt, który wysyła powiadomienia (provider); obiekt Observer reprezentuje klasę, która je odbiera (observer). Klasa Subject dziedziczy zarówno obserwowalny jak I Obserwator, w tym sensie, że jest zarówno obserwatorem jak i obserwowalne. Możesz użyć tematu, aby subskrybować wszystkich obserwatorów, a następnie subskrybować temat do źródła danych backendu
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();

Więcej o https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/subjects.md

 20
Author: Md Ayub Ali Sarker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-14 15:27:14

Jednej rzeczy nie widzę w przykładach jest to, że kiedy rzucasz BehaviorSubject do Observable przez asObservable, dziedziczy zachowanie zwracania ostatniej wartości w subskrypcji.

To trudna sprawa, ponieważ biblioteki często ujawniają pola jako obserwowalne (np. params w ActivatedRoute w Angular2), ale mogą używać Subject lub BehaviorSubject za kulisami. To, czego używają, wpłynęłoby na zachowanie subskrypcji.

Zobacz tutaj 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);
 12
Author: Lukasz Marek Sielski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-05 14:59:04

An observable pozwala na subskrybowanie tylko, podczas gdy temat pozwala zarówno publikować, jak i subskrybować.

Więc temat pozwala } usługi być używane zarówno jako wydawca i abonent.

Jak na razie, nie jestem tak dobry w Observable, więc podzielę się tylko przykładem Subject.

Zrozummy lepiej za pomocą przykładukątowego CLI . Uruchom poniższe polecenia:

npm install -g @angular/cli

ng new angular2-subject

cd angular2-subject

ng serve

Zastąp zawartość app.component.html z:

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

<app-home>
</app-home>

Uruchom polecenie ng g c components/home, aby wygenerować komponent home. Zamień Zawartość home.component.html na:

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

#message jest zmienną lokalną. Dodaj właściwość message: string; do klasy app.component.ts.

Uruchom to polecenie ng g s service/message. Spowoduje to wygenerowanie usługi pod adresem src\app\service\message.service.ts. Udostępnij tę usługę aplikacji .

Importuj Subject do MessageService. Dodaj też temat. Kod końcowy wygląda następująco:

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

Teraz wstrzyknij tę usługę w home.component.ts i zdaj instancja tego do konstruktora. Zrób to dla app.component.ts też. Użyj tej instancji usługi do przekazania wartości #message do funkcji usługi 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);
  }
}
Aby zapobiec wyciekom pamięci, zapisz się i Anuluj subskrypcję (aby zapobiec wyciekom pamięci) do 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();
  }
}

To jest to.

Teraz każda wartość wpisana wewnątrz #message z home.component.html zostanie wydrukowana do {{message}} wewnątrz app.component.html

 4
Author: student,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-06 06:17:18