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