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?
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.
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 │
└─────────────────────────────────────┴─────────────────────────────────────┘
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 backenduvar 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
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);
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
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