Поведение субъекта против наблюдаемого?
Я изучаю паттерны Angular RxJs и не понимаю разницу между BehaviorSubject
и Observable
,
Из моего понимания, BehaviorSubject
это значение, которое может изменяться с течением времени (может быть подписано и подписчики могут получать обновленные результаты). Это похоже на ту же цель Observable
,
Когда бы вы использовали Observable
против BehaviorSubject
? Есть ли преимущества в использовании BehaviorSubject
за Observable
или наоборот?
14 ответов
BehaviorSubject - это тип субъекта, субъект - это особый тип наблюдаемых, поэтому вы можете подписываться на сообщения, как и любые другие наблюдаемые. Уникальные особенности BehaviorSubject:
- Ему нужно начальное значение, поскольку оно всегда должно возвращать значение в подписке, даже если оно не получило
next()
- При подписке возвращает последнее значение темы. Обычная наблюдаемая срабатывает только тогда, когда она получает
onnext
- в любой момент вы можете получить последнее значение объекта в ненаблюдаемом коде, используя
getValue()
метод.
Уникальные особенности предмета по сравнению с наблюдаемым:
- Это наблюдатель в дополнение к тому, что он является наблюдаемым, поэтому вы также можете отправлять значения субъекту в дополнение к подписке на него.
Кроме того, вы можете получить наблюдаемое от субъекта поведения, используя asObservable()
метод на BehaviorSubject
,
Наблюдаемый является родовым, и BehaviorSubject
технически является подтипом Observable, потому что 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()
метод.
В сервисах Angular я бы использовал BehaviorSubject
для службы данных как угловая служба часто инициализируется до того, как компонент и субъект поведения гарантируют, что компонент, потребляющий услугу, получит последние обновленные данные, даже если нет новых обновлений с момента подписки компонента на эти данные.
Наблюдаемые: разные результаты для каждого наблюдателя
Одно очень очень важное отличие. Поскольку Observable - это просто функция, у нее нет состояния, поэтому для каждого нового Observer он снова и снова выполняет наблюдаемый код создания. Это приводит к:
Код запускается для каждого наблюдателя. Если это вызов HTTP, он вызывается для каждого наблюдателя
Это вызывает серьезные ошибки и неэффективность
BehaviorSubject (или Subject) хранит данные наблюдателя, запускает код только один раз и передает результат всем наблюдателям.
Пример:
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>
Выход:
"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 │
└─────────────────────────────────────┴─────────────────────────────────────┘
Наблюдаемый и субъект оба являются наблюдаемыми средствами, которые наблюдатель может отслеживать. но оба они имеют некоторые уникальные характеристики. Далее есть всего 3 типа предметов, каждый из которых снова имеет уникальные характеристики. Давайте попробуем понять каждого из них.
Вы можете найти практический пример здесь на stackblitz. (Вам нужно проверить консоль, чтобы увидеть фактический вывод)
Observables
Они холодные: код выполняется в списке, у которого есть один наблюдатель.
Создает копию данных: Observable создает копию данных для каждого наблюдателя.
Однонаправленный: наблюдатель не может присвоить значение наблюдаемому (происхождение / мастер).
Subject
Они горячие: код выполняется и значение транслируется, даже если нет наблюдателя.
Данные об акциях: одни и те же данные передаются всем наблюдателям.
двунаправленный: наблюдатель может присвоить значение наблюдаемому (источник / мастер).
Если вы используете использование темы, то вы пропустите все значения, которые транслируются до создания наблюдателя. так что здесь приходит Replay Subject
Replay Subject
Они горячие: код выполняется и значение транслируется, даже если нет наблюдателя.
Данные об акциях: одни и те же данные передаются всем наблюдателям.
двунаправленный: наблюдатель может присвоить значение наблюдаемому (источник / мастер). плюс
Воспроизведение потока сообщений. Независимо от того, когда вы подпишетесь на тему воспроизведения, вы будете получать все передаваемые сообщения.
В теме и в теме воспроизведения вы не можете установить начальное значение на наблюдаемое. Так что вот идет Поведенческий предмет
Behavioral Subject
Они горячие: код выполняется и значение транслируется, даже если нет наблюдателя.
Данные об акциях: одни и те же данные передаются всем наблюдателям.
двунаправленный: наблюдатель может присвоить значение наблюдаемому (источник / мастер). плюс
Воспроизведение потока сообщений. Независимо от того, когда вы подпишетесь на тему воспроизведения, вы будете получать все передаваемые сообщения.
Вы можете установить начальное значение: Вы можете инициализировать наблюдаемое значение по умолчанию.
Объект Observable представляет коллекцию на основе push.
Интерфейсы Observer и Observable предоставляют обобщенный механизм для push-уведомлений, также известный как шаблон проектирования наблюдателя. Объект Observable представляет объект, который отправляет уведомления (поставщик); объект 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, он наследует поведение возврата последнего значения в подписке.
Это сложный момент, поскольку библиотеки часто выставляют поля как наблюдаемые (то есть параметры в ActivatedRoute в Angular2), но могут использовать Subject или BehaviorSubject за кулисами. То, что они используют, повлияет на поведение подписки.
Смотрите здесь 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);
Наблюдаемое позволяет вам подписываться только тогда, когда тема позволяет вам как публиковать, так и подписываться.
Таким образом, субъект позволяет использовать ваши услуги как издателя, так и подписчика.
На данный момент я не так хорош в Observable
так что я поделюсь только примером Subject
,
Давайте лучше разберемся с примером Angular 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.component.html
с:
<input type="text" placeholder="Enter message" #message>
<button type="button" (click)="setMessage(message)" >Send message</button>
#message
здесь локальная переменная Добавить недвижимость message: string;
к app.component.ts
Класс.
Запустите эту команду 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
из home.component.html
должен быть напечатан {{message}}
внутри app.component.html
Думайте о Observables как о трубе, в которой течет вода, иногда вода течет, а иногда нет. В некоторых случаях вам может действительно понадобиться труба, в которой всегда есть вода, вы можете сделать это, создав специальную трубу, которая всегда содержит воду, независимо от того, насколько она мала, давайте назовем эту специальную трубу BehaviorSubject, если вам случится поставщик водоснабжения в вашем районе, вы можете спокойно спать по ночам, зная, что ваша недавно установленная труба просто работает.
С технической точки зрения: вы можете столкнуться со случаями использования, в которых Observable всегда должен иметь значение, возможно, вы хотите зафиксировать значение входного текста с течением времени, затем вы можете создать экземпляр BehaviorSubject, чтобы гарантировать такое поведение, скажем:
const firstNameChanges = new BehaviorSubject("<empty>");
// pass value changes.
firstNameChanges.next("Jon");
firstNameChanges.next("Arya");
Затем вы можете использовать "значение" для выборки изменений во времени.
firstNameChanges.value;
Это пригодится, когда вы позже комбинируете Observables, взглянув на тип вашего потока как BehaviorSubject, вы можете убедиться, что поток хотя бы запускается или сигнализирует хотя бы один раз.
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
});
BehaviorSubject vs Observable: RxJS имеет наблюдателей и наблюдаемых, Rxjs предлагает несколько классов для использования с потоками данных, и один из них - BehaviorSubject.
Observables: Observables - это ленивые коллекции нескольких значений с течением времени.
BehaviorSubject: субъект, которому требуется начальное значение и который выдает текущее значение новым подписчикам.
// 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
Observable - это общий,
Наблюдаемые - это ленивые коллекции нескольких значений с течением времени.
Это просто функция, без состояния
Выполнение кода для каждого наблюдателя
BehaviorSubject : Тема, которая требует начального значения и передает свое текущее значение новым подписчикам.
технически является подтипом Observable, потому что BehaviorSubject - это наблюдаемое с определенными качествами.
Имеет состояние. Хранит данные в памяти
Один и тот же код запускается только один раз для всех наблюдателей
Уникальные особенности BehaviorSubject следующие:
Ему нужно начальное значение, так как он всегда должен возвращать значение по подписке, даже если он не получил next()
При подписке возвращает последнее значение темы. Обычное наблюдаемое срабатывает, только когда получает onnext.
в любой момент вы можете получить последнее значение объекта в ненаблюдаемом коде с помощью метода getValue() .
Субъект в rxjs, по сути, представляет собой смесь наблюдателя и наблюдаемого. Наблюдатель — это то, что мы добавляем в значения, наблюдаемое — это то, что мы можем наблюдать за значениями.
- Тема по умолчанию Горячая. Наблюдаемые по умолчанию холодные. Это означает, что они не будут выдавать никаких значений, пока кто-то не подпишется на них. В тот момент, когда мы создаем субъект, мы можем выдать из него значение, и это значение будет выдано, даже если на него еще никто не подписан.
- Тема многоадресная по умолчанию. Наблюдаемые по умолчанию являются одноадресными, и это означает, что для каждого другого наблюдателя, который у нас есть, мы должны подписаться на наблюдаемый объект, если этот наблюдаемый выдает значение, которое будет проходить через все различные операторы внутри нашего канала один раз для каждого подписчика. . Многоадресная рассылка означает, что все остальные операторы будут выполняться один раз для каждого значения, независимо от количества имеющихся у нас наблюдателей.
- GOTCHA= SUBJECT является многоадресным, но если вы соедините с ним оператор канала, это вернет новый наблюдаемый объект, который является холодным и одноадресным.
Субъект поведения такой же, как и субъект, но также принимает начальное «начальное» значение. Новые подписчики мгновенно получают самое последнее значение. Если кто-то когда-либо подпишется на тему «Поведение», он мгновенно получит самое последнее значение. Таким образом, предмет поведения всегда будет иметь некоторую ценность для подписчика.
Самое полезное в предмете поведения — это когда мы начинаем делать сетевые запросы. Представьте, что мы привязали некоторые элементы конвейера к субъекту поведения, а внутри функции канала или оператора канала мы в конечном итоге делаем сетевой запрос и извлекаем некоторые данные. В конечном итоге вы можете захотеть, чтобы что-то еще подписалось на этот наблюдаемый объект и сразу же получило данные, которые уже были получены. Используя субъект поведения, мы можем легко реализовать такое поведение.
Я думаю, что Observable является оболочкой для предмета. Пока Observable используется только для подписки на изменения данных. Тема также может использоваться для уведомления подписчиков об изменениях данных (используя метод next()). Вот небольшая реализация наблюдаемого шаблона, которая может помочь вам понять концепцию. Площадка для TypeScript
BehaviorSubject
BehaviorSubject основан на той же функциональности, что и наш ReplaySubject, subject like, hot и воспроизводит предыдущее значение.
BehaviorSubject добавляет еще одну функциональность: вы можете дать BehaviorSubject начальное значение. Давайте посмотрим на этот код
import { ReplaySubject } from 'rxjs';
const behaviorSubject = new BehaviorSubject(
'hello initial value from BehaviorSubject'
);
behaviorSubject.subscribe(v => console.log(v));
behaviorSubject.next('hello again from BehaviorSubject');
Наблюдаемые
Для начала рассмотрим минимальный API для создания обычного Observable. Есть несколько способов создать Observable. Мы создадим Observable путем создания экземпляра класса. Другие операторы могут упростить это, но мы захотим сравнить шаг создания экземпляра с нашими различными наблюдаемыми типами.
import { Observable } from 'rxjs';
const observable = new Observable(observer => {
setTimeout(() => observer.next('hello from Observable!'), 1000);
});
observable.subscribe(v => console.log(v));
Просто чтобы быть ясным, вы также можете изменить объект на наблюдаемый следующим образом:
page = new BehaviorSubject<String|null>(null);
actualPage:Observable<string> = new Observable()
this.page.next("hardware")
this.actualPage = this.page as Observable<any>;