BehaviorSubject vs Спостерігається?


690

Я переглядаю шаблони кутових RxJs і не розумію різниці між a BehaviorSubjectі an Observable.

З мого розуміння, a BehaviorSubject- це значення, яке може змінюватися з часом (можна передплатити, а абоненти можуть отримувати оновлені результати). Це здається точно такою самою метою Observable.

Коли ви використовуєте Observablevs BehaviorSubject? Чи є переваги використання BehaviorSubjectнад Observableабо навпаки?

Відповіді:


969

BehaviorSubject - це тип теми, тема - це особливий тип спостережуваного, щоб ви могли підписатися на повідомлення, як і будь-яке інше спостережуване. Унікальними рисами BehaviorSubject є:

  • Він потребує початкового значення, оскільки він завжди повинен повертати значення підписки, навіть якщо він не отримав next()
  • Після підписки він повертає останнє значення теми. Регулярне спостереження запускається лише тоді, коли воно отримує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для послуги даних, оскільки кутова служба часто ініціалізується перед тим, як компонент, а суб'єкт поведінки гарантує, що компонент, що споживає послугу, отримує останні оновлені дані, навіть якщо немає нових оновлень з моменту підписки компонента на ці дані.


7
Я трохи плутаюсь із прикладом 2 звичайної теми. Чому підписка не отримає нічого, навіть у другому рядку, який ви надсилаєте значення предмету, використовуючи subject.next ("b")?
jmod999

25
@ jmod999 Другий приклад - це звичайний суб'єкт, який отримує значення прямо до виклику підписки. У звичайних об'єктах підписка спрацьовує лише для значень, отриманих після виклику підписки. Оскільки а отримано безпосередньо перед передплатою, він не надсилається до підписки.
Шантану Бхадорія

Примітка про це фантастичне рішення, якщо ви використовуєте це у функції та повертаєте його, тоді повертайте спостережуване. У мене були деякі проблеми з поверненням предмета, і це збиває з пантелику інших розробників , які тільки знають , що таке Спостережувані
СЕМ

8
У мене в середу було інтерв'ю Angular 4. Оскільки я все ще вивчаю нову платформу, він спонукав мене, запитуючи мене щось на кшталт "Що буде, якщо я підпишусь на спостережуване, яке знаходиться в модулі, який ще не ліниво завантажений?" Я не був впевнений, але він сказав мені, що відповідь - використовувати BSubject - саме так, як пан Бхадорія пояснив це вище. Відповідь полягала в тому, щоб використовувати BSubject, оскільки він завжди повертає останнє значення (принаймні, так я пам’ятаю остаточний коментар інтерв'юера з цього приводу).
bob.mazzo

1
@ bob.mazzo Чому для цього потрібно використовувати BSubject? - Якщо я підписався на цього спостерігача, я нічого не отримаю, тому що спостерігач не був ініціалізований, тому він не може надсилати дані спостерігачам. Якщо я використовую BSubject, я не отримаю нічого з тієї ж причини. В обох випадках абонент нічого не отримає, оскільки знаходиться в модулі, який не був ініціалізований. Чи правий я?
Рафаель Рейєс

183

Спостерігається: різний результат для кожного спостерігача

Одна дуже важлива різниця. Оскільки Observable - це лише функція, вона не має жодного стану, тому для кожного нового спостерігача він виконує коди створення, які можна спостерігати, знову і знову. Це призводить до:

Код виконується для кожного спостерігача. Якщо його 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 Спостережуване: метод підписки завжди запускає метод onNext, пов'язаний з спостерігачем, і приносить повернене значення. BehaviourSubject / Subject: завжди буде повертати останнє значення в потоці. тут метод підписки з предметом не запустить onNext метод його Observer, поки він не знайде останнє значення в потоці.
Мохан Рам

62

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

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

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

Observables

Вони холодні: Код виконується, коли у них є хоча б один спостерігач.

Створює копію даних: Спостерігається створює копію даних для кожного спостерігача.

Односторонній: спостерігач не може присвоїти значення спостережуваному (походження / головний).

Subject

Вони гарячі: код виконується, а значення транслюється, навіть якщо спостерігача немає.

Дані про спільний доступ : однакові дані діляться між усіма спостерігачами.

двонаправлений: спостерігач може призначити значення спостережуваному (походження / головний).

Якщо ви використовуєте тему, то ви пропускаєте всі значення, які транслюються перед створенням спостерігача. Отож тут виходить Replay Subject

ReplaySubject

Вони гарячі: код виконується, а значення отримують трансляцію, навіть якщо спостерігача немає.

Дані про спільний доступ : однакові дані діляться між усіма спостерігачами.

двонаправлений: спостерігач може призначити значення спостережуваному (походження / головний). плюс

Повторіть потік повідомлень: Незалежно від того, коли ви підписуєте тему повторного відтворення, ви отримаєте всі трансляційні повідомлення.

У темі та предметі відтворення ви не можете встановити початкове значення для спостережуваного. Отож, тут з'являється тема поведінки

BehaviorSubject

Вони гарячі: код виконується, а значення отримують трансляцію, навіть якщо спостерігача немає.

Дані про спільний доступ : однакові дані діляться між усіма спостерігачами.

двонаправлений: спостерігач може призначити значення спостережуваному (походження / головний). плюс

Повторіть потік повідомлень: Незалежно від того, коли ви підписуєте тему повторного відтворення, ви отримаєте всі трансляційні повідомлення.

Ви можете встановити початкове значення: Ви можете ініціалізувати спостережуване зі значенням за замовчуванням.


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

28

Об'єкт, що спостерігається, являє собою колекцію на основі поштовху.

Інтерфейси спостерігача та спостережуваного пристрою забезпечують узагальнений механізм повідомлення на основі натискання, також відомий як модель дизайну спостерігача. Об'єкт, що спостерігається, представляє об'єкт, який надсилає сповіщення (постачальник); об’єкт спостерігача представляє клас, який їх отримує (спостерігач).

Клас Subject успадковує як Opable, так і 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 ()?
вибір - Джек Бао

4
@choopage різниці немає. останній новий шлях
Рой Намір

Якщо скасувати передплату перед тим, як суб'єкт розпоряджається, інакше підписка стає сміттям, оскільки вона підписується на нульове значення.
Софі Чжан

20

Одне, що я не бачу в прикладах, це те, що коли ви передаєте BehaviorSubject до Observable через asObservable, він успадковує поведінку повернення останнього значення підписки.

Це складний біт, оскільки часто бібліотеки будуть виставляти поля як спостережувані (тобто парами в ActivateRoute в 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.

Розберемося краще на прикладі кутового 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's.

Виконайте цю команду 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

@ruffin Це лише середня відповідь із середньою кількістю голосів, подивіться на мій профіль. Не обов'язково голосування: D
Мохаммед Замер

1
Я давав вам пропозицію раніше, але ви ухилилися від питання, чому зображення там. Це не пов'язане безпосередньо з вашою відповіддю. Не має значення, чи є у вас багато повторень чи ні - якщо зображення не прямо та конкретно з’ясовано, я б просив вас його видалити . /
знизати

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 проти спостерігається : RxJS має спостерігач і спостерігаються, Rxjs пропонує широкі кілька класів для використання з потоками даних, і один з них є BehaviorSubject.

Спостереження : спостереження - це ліниві колекції декількох значень у часі.

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 , якщо вам трапиться постачальник водопостачання у вашій громаді, ви можете спокійно спати вночі, знаючи, що ваша щойно встановлена ​​труба просто працює.

У технічному плані: ви можете зіткнутися з використанням випадків використання, коли спостерігається завжди має значення в ньому, можливо, ви хочете з часом захопити значення вхідного тексту, ви можете створити екземпляр BehaviorSubject для забезпечення такого типу поведінки, скажімо:


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

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

Потім можна використовувати "значення" для вибірки змін у часі.


firstNameChanges.value;

Це стане в нагоді, коли ви комбінуєте спостерігаючі пізніше, подивившись тип вашого потоку як BehaviorSubject, ви зможете переконатися, що потік принаймні спрацьовує або подається сигнал лише один раз .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.