BehaviorSubject vs Observable?
Я смотрю на угловые шаблоны RxJs, и я не понимаю разницы между BehaviorSubject
и Observable
.
из моего понимания, a BehaviorSubject
- Это значение, которое может меняться со временем (может быть подписано и подписчики могут получать обновленные результаты). Кажется, это та же самая цель Observable
.
когда вы используете Observable
vs a BehaviorSubject
? Есть ли преимущества в использовании BehaviorSubject
на Observable
или наоборот?
5 ответов
BehaviorSubject - это тип субъекта, субъект-это особый тип наблюдаемого, поэтому вы можете подписаться на сообщения, как и любые другие наблюдаемые. Уникальными особенностями BehaviorSubject являются:
- он должен начальное значение так как он всегда должен возвращать значение по подписке, даже если он не получил
next()
- при подписке он возвращает последнее значение темы. Регулярное observable только вызывает когда оно получает
onnext
- в любой момент Вы можете получить последнее значение субъекта в ненаблюдаемом коде, используя
getValue()
метод.
уникальные особенности предмета по сравнению с наблюдаемым:
- это наблюдатель в дополнение к наблюдаемому, поэтому вы также можете отправлять значения субъекту в дополнение к подписке на него.
кроме того, вы можете получить наблюдаемый от субъекта поведения, используя asObservable()
метод BehaviorSubject
.
наблюдаемых является общим, и BehaviorSubject
технически является подтипом наблюдаемого, потому что BehaviorSubject является наблюдаемым с определенными качествами.
пример 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
Пример 2 с обычной темой:
// 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
наблюдаемый может быть создан из обоих Subject
и BehaviorSubject
используя subject.asObservable()
.
единственная разница в том, что вы не можете отправить значения для наблюдаемого использования next()
метод.
в угловых службах я бы использовал BehaviorSubject
для службы данных как угловой службы часто инициализируется перед компонентом и субъектом поведения гарантирует, что компонент, потребляющий службу, получает последние обновленные данные, даже если нет новых обновлений с момента подписки компонента на эти данные.
Observable: различный результат для каждого наблюдателя
одно очень важное различие. Поскольку Observable-это просто функция, она не имеет никакого состояния, поэтому для каждого нового наблюдателя она снова и снова выполняет наблюдаемый код create. Это приводит к:
код запускается для каждого наблюдателя . Если это HTTP-вызов, он вызывается для каждого наблюдателя
это вызывает основные ошибки и неэффективности
BehaviorSubject (или субъект ) сохраняет данные наблюдателя, запускает код только один раз и дает результат всем наблюдателям .
Ex:
JSBin:http://jsbin.com/qowulet/edit?js, консоль
// --- 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>
выход :
"observer 1: 0.7184075243594013"
"observer 2: 0.41271850211336103"
"observer subject 1: 0.8034263165479893"
"observer subject 2: 0.8034263165479893"
наблюдать, как с помощью Observable.create
создал разные выходные данные для каждого наблюдателя, но BehaviorSubject
дал тот же результат для всех наблюдателей. Это важно.
другие различия резюмировать.
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ 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 │
└─────────────────────────────────────┴─────────────────────────────────────┘
наблюдаемый объект представляет коллекцию на основе push.
интерфейсы Observer и Observable предоставляют обобщенный механизм для push-уведомлений, также известный как шаблон проектирования observer. Наблюдаемый объект представляет объект, который отправляет уведомления (поставщик); объект наблюдателя представляет класс, который их получает (наблюдатель).
класс Subject наследует как Observable, так и Observer в том смысле, что он является наблюдатель и наблюдаемый. Вы можете использовать тему, чтобы подписаться на всех наблюдателей, а затем Подписаться на тему в бэкэнд-источнике данных
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();
Подробнее https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/subjects.md
одна вещь, которую я не вижу в примерах, заключается в том, что когда вы бросаете BehaviorSubject в Observable через asObservable, он наследует поведение возврата последнего значения по подписке.
Это сложный бит, так как часто библиотеки будут выставлять поля как наблюдаемые (т. е. params в ActivatedRoute в Angular2), но могут использовать тему или BehaviorSubject за кулисами. То, что они используют, повлияет на поведение подписки.
см. здесь http://jsbin.com/ziquxapubo/edit?html,js, консоль
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);
An наблюдаемых позволяет подписаться только тогда как теме позволяет публиковать и подписываться.
так что тема позволяет ваш услуги используется как издатель, так и подписчик.
на данный момент я не так хорош в Observable
поэтому я поделюсь только пример Subject
.
давайте лучше разберемся с угловой CLI пример. Запустите ниже команды:
npm install -g @angular/cli
ng new angular2-subject
cd angular2-subject
ng serve
заменить содержимое app.component.html
С:
<div *ngIf="message">
{{message}}
</div>
<app-home>
</app-home>
выполнить команду ng g c components/home
для создания компонента home. Заменить содержимое home.component.html
С:
<input type="text" placeholder="Enter message" #message>
<button type="button" (click)="setMessage(message)" >Send message</button>
#message
является локальной переменной здесь. Добавить свойство message: string;
до 'ы.
выполнить команду ng g s service/message
. Это создаст сервис по адресу src\app\service\message.service.ts
. Обеспечить эта услуга для приложения.
импорт Subject
в MessageService
. Добавить тему. Окончательный код должен выглядеть так:
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
}
}
теперь введите эту услугу в home.component.ts
и передать экземпляр его конструктору. Сделайте это для app.component.ts
тоже. Используйте этот экземпляр службы для передачи значения #message
функции службы 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);
}
}
внутри app.component.ts
, подписаться и отписаться (для предотвращения утечек памяти) к 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();
}
}
вот и все.
Итак, любое значение вошел внутрь #message
of home.component.html
печатается на {{message}}
внутри app.component.html