Поведение субъекта против наблюдаемого?


690

Я изучаю паттерны Angular RxJ и не понимаю разницы между a BehaviorSubjectи an Observable.

Насколько я понимаю, BehaviorSubjectзначение a может со временем меняться (на него можно подписаться и подписчики могут получать обновленные результаты). Кажется, это точно такая же цель Observable.

Когда вы будете использовать Observableпротив BehaviorSubject? Есть ли преимущества использования BehaviorSubjectболее Observableили наоборот?

Ответы:


970

BehaviorSubject - это тип субъекта, субъект - это особый тип наблюдаемых, поэтому вы можете подписываться на сообщения, как и любые другие наблюдаемые. Уникальные особенности BehaviorSubject:

  • Ему нужно начальное значение, поскольку оно всегда должно возвращать значение в подписке, даже если оно не получило next()
  • При подписке возвращает последнее значение темы. Обычная наблюдаемая срабатывает только тогда, когда она получаетonnext
  • в любой момент вы можете извлечь последнее значение субъекта в ненаблюдаемом коде, используя getValue()метод.

Уникальные особенности предмета по сравнению с наблюдаемым:

  • Это наблюдатель в дополнение к тому, что он является наблюдаемым, поэтому вы также можете отправлять значения субъекту в дополнение к подписке на него.

Кроме того, вы можете получить наблюдаемое от субъекта поведения, используя asObservable()метод on BehaviorSubject.

Observable является универсальным и 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службу данных, так как служба Angular часто инициализируется до того, как компонент и субъект поведения гарантируют, что компонент, использующий сервис, получит последние обновленные данные, даже если нет новых обновлений с момента подписки компонента на эти данные.


7
Я немного запутался в примере 2 обычной темы. Почему подписка не получит ничего, даже если во второй строке вы отправляете значения субъекту, используя subject.next ("b")?
jmod999

25
@ jmod999 Второй пример - обычный субъект, который получает значение непосредственно перед вызовом подписки. В обычных темах подписка запускается только для значений, полученных после вызова подписки. Поскольку a получено непосредственно перед подпиской, оно не отправляется в подписку.
Шантану Бхадория

Замечание об этом фантастическом решении: если вы используете его в функции и возвращаете его, возвращаете наблюдаемое. У меня были некоторые проблемы с возвращением темы, и это сбивает с толку других разработчиков, которые знают только, что такое Observables
Сэм

8
У меня было интервью Angular 4 в среду. Так как я все еще изучаю новую платформу, он споткнул меня, спросив меня что-то вроде: «Что произойдет, если я подпишусь на наблюдаемое, которое находится в модуле, который еще не загружен с отложенной загрузкой?» Я не был уверен, но он сказал мне, что ответ должен был использовать BSubject - ТОЧНО, как мистер Бхадориа объяснил это выше. Ответ состоял в том, чтобы использовать BSubject, потому что он всегда возвращает последнее значение (по крайней мере, так я помню последний комментарий интервьюера по этому поводу).
bob.mazzo

1
@ bob.mazzo Почему мне нужно использовать BSubject для этого случая? - Если я подпишусь на этого Обозревателя, я ничего не получу, потому что наблюдатель не был инициализирован, поэтому он не может передавать данные наблюдателям, и если я использую объект BSubject, я тоже не получу ничего по той же причине. В обоих случаях подписчик ничего не получит, потому что находится внутри модуля, который не был инициализирован. Я прав?
Рафаэль Рейес

183

Наблюдаемые: разные результаты для каждого наблюдателя

Одно очень очень важное отличие. Поскольку 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
любой, кто придет KnockoutJS's ko.observable(), сразу увидит больше параллелей по Rx.BehaviorSubjectсравнению сRx.Observable
Simon_Weaver

@Skeptor Observable: метод подписки всегда запускает метод onNext, связанный с наблюдателем, и возвращает возвращаемое значение. BehaviourSubject / Subject: всегда будет возвращать последнее значение в потоке. здесь метод subscribee с субъектом не будет вызывать метод onNext своего наблюдателя, пока не найдет последнее значение в потоке.
Мохан Рам

62

Наблюдаемый и субъект оба являются наблюдаемыми средствами, которые наблюдатель может отслеживать. но оба они имеют некоторые уникальные характеристики. Далее есть всего 3 типа предметов, каждый из которых снова имеет уникальные характеристики. Давайте попробуем понять каждого из них.

Вы можете найти практический пример здесь на stackblitz . (Вам нужно проверить консоль, чтобы увидеть фактический вывод)

введите описание изображения здесь

Observables

Они холодные: код выполняется, когда у них есть хотя бы один наблюдатель.

Создает копию данных: Observable создает копию данных для каждого наблюдателя.

Однонаправленный: наблюдатель не может присвоить значение наблюдаемому (источник / мастер).

Subject

Они горячие: код выполняется и значение транслируется, даже если нет наблюдателя.

Данные об акциях: одни и те же данные передаются всем наблюдателям.

двунаправленный: наблюдатель может присвоить значение наблюдаемому (источник / мастер).

Если вы используете использование темы, то вы пропустите все значения, которые транслируются до создания наблюдателя. Так что вот идет Replay Subject

ReplaySubject

Они горячие: код выполняется и значение транслируется, даже если нет наблюдателя.

Данные об акциях: одни и те же данные передаются всем наблюдателям.

двунаправленный: наблюдатель может присвоить значение наблюдаемому (источник / мастер). плюс

Воспроизведение потока сообщений. Независимо от того, когда вы подпишитесь на тему воспроизведения, вы будете получать все передаваемые сообщения.

В теме и воспроизведении темы вы не можете установить начальное значение на наблюдаемое. Итак, здесь идет Поведенческий предмет

BehaviorSubject

Они горячие: код выполняется и значение транслируется, даже если нет наблюдателя.

Данные об акциях: одни и те же данные передаются всем наблюдателям.

двунаправленный: наблюдатель может присвоить значение наблюдаемому (источник / мастер). плюс

Воспроизведение потока сообщений. Независимо от того, когда вы подпишитесь на тему воспроизведения, вы будете получать все передаваемые сообщения.

Вы можете установить начальное значение: Вы можете инициализировать наблюдаемое значение по умолчанию.


3
Стоит упомянуть, что a ReplaySubjectимеет историю и может передавать / излучать последовательность (старых) значений. Только когда буфер установлен в 1, он ведет себя подобно a BehaviorSubject.
Уилт

28

Объект 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


В чем разница между subscription.dispose () и subscription.unsubscribe ()?
choopage - Джек Бао

4
@ hoopage без разницы. последний новый путь
Ройи Намир

Если отменить подписку до удаления субъекта, в противном случае подписка становится мусором, поскольку она подписывается на нулевое значение.
Софи Чжан

20

Одна вещь, которую я не вижу в примерах, это то, что когда вы приводите 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);

11

Наблюдаемые позволяет подписаться только в то время как субъект позволяет как публиковать и подписаться.

Таким образом, субъект позволяет использовать ваши услуги как издателя, так и подписчика.

На данный момент я не очень хорош в этом, 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


Почему гигантский образ? Если это не имеет прямого отношения к вашему ответу, это похоже на приманку.
Ерф

@ruffin Это просто средний ответ со средним количеством голосов, посмотрите мой профиль. Не совсем голословная приманка: D
Мухаммед Замир

1
Я дал вам голос ранее, но вы уклонились от вопроса, почему изображение там. Это не имеет прямого отношения к вашему ответу. Не имеет значения, много ли у вас повторений или нет - если изображение не является прямо и конкретно объяснительным, я бы попросил вас удалить его . / плечами
ruffin

1
@ruffin Если это идет вразрез с согласием сообщества, то это не должно быть там наверняка!
Мухаммед Замир

4

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

1

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

1

Думайте о наблюдаемых как о трубе с проточной водой, иногда вода течет, а иногда нет. В некоторых случаях вам может фактически понадобиться труба, в которой всегда есть вода, вы можете сделать это, создав специальную трубу, которая всегда содержит воду, независимо от того, насколько она мала, давайте назовем эту специальную трубу BehaviorSubject , если вы оказались Поставщик водоснабжения в вашем районе, вы можете спокойно спать по ночам, зная, что только что установленная трубка просто работает.

С технической точки зрения: вы можете столкнуться с сценариями использования, где Observable всегда должен иметь значение, возможно, вы захотите зафиксировать значение входного текста с течением времени, затем вы можете создать экземпляр BehaviorSubject для обеспечения такого поведения, например:


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

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

Затем вы можете использовать «значение» для выборки изменений во времени.


firstNameChanges.value;

Это пригодится, когда вы объедините Observables позже, взглянув на тип вашего потока как BehaviorSubject, и вы сможете убедиться, что поток хотя бы один раз запускается или сигнализирует хотя бы один раз .

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.