BehaviorSubject vs Observable?


690

Saya melihat ke dalam pola Angular RxJs dan saya tidak mengerti perbedaan antara a BehaviorSubjectdan a Observable.

Dari pemahaman saya, a BehaviorSubjectadalah nilai yang dapat berubah dari waktu ke waktu (dapat berlangganan dan pelanggan dapat menerima hasil yang diperbarui). Ini tampaknya menjadi tujuan yang sama persis Observable.

Kapan Anda akan menggunakan Observablevs a BehaviorSubject? Apakah ada manfaat menggunakan BehaviorSubjectlebih dari satu Observableatau sebaliknya?

Jawaban:


969

BehaviorSubject adalah jenis subjek, subjek adalah jenis khusus yang dapat diamati sehingga Anda dapat berlangganan pesan seperti yang dapat diamati lainnya. Fitur unik BehaviorSubject adalah:

  • Dibutuhkan nilai awal karena harus selalu mengembalikan nilai pada berlangganan bahkan jika belum menerima next()
  • Setelah berlangganan, ia mengembalikan nilai terakhir dari subjek. Yang diamati secara teratur hanya memicu ketika menerimaonnext
  • kapan saja, Anda dapat mengambil nilai terakhir dari subjek dalam kode yang tidak dapat diamati menggunakan getValue()metode ini.

Fitur unik dari subjek dibandingkan dengan yang dapat diamati adalah:

  • Ini adalah pengamat selain menjadi yang dapat diamati sehingga Anda juga dapat mengirim nilai ke subjek selain berlangganan.

Selain itu, Anda bisa melihat subjek perilaku yang dapat diamati menggunakan asObservable()metode ini BehaviorSubject.

Dapat diobservasi adalah Generik, dan BehaviorSubjectsecara teknis merupakan sub-tipe yang Dapat Diobservasi karena BehaviorSubject dapat diamati dengan kualitas spesifik.

Contoh dengan 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

Contoh 2 dengan subjek reguler:

// 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

Terobservasi dapat dibuat dari keduanya Subjectdan BehaviorSubjectmenggunakan subject.asObservable().

Satu-satunya perbedaan adalah Anda tidak dapat mengirim nilai ke next()metode menggunakan diamati .

Dalam layanan Angular, saya akan menggunakan BehaviorSubjectlayanan data sebagai layanan sudut yang sering diinisialisasi sebelum komponen dan subjek perilaku memastikan bahwa komponen yang mengonsumsi layanan menerima data terakhir yang diperbarui bahkan jika tidak ada pembaruan baru sejak komponen berlangganan data ini.


7
Saya sedikit bingung dengan contoh 2 subjek reguler. Mengapa langganan tidak akan mendapatkan apa-apa bahkan pada baris kedua Anda mengirim nilai ke subjek menggunakan subject.next ("b")?
jmod999

25
@ jmod999 Contoh kedua adalah subjek reguler yang menerima nilai tepat sebelum berlangganan dipanggil. Dalam mata pelajaran reguler, langganan hanya dipicu untuk nilai yang diterima setelah berlangganan dipanggil. Karena a diterima tepat sebelum berlangganan, itu tidak dikirim ke berlangganan.
Shantanu Bhadoria

Catatan tentang solusi luar biasa itu, jika Anda menggunakannya dalam suatu fungsi dan mengembalikannya, maka kembalikan yang bisa diamati. Saya punya beberapa masalah dengan mengembalikan subjek, dan membingungkan pengembang lain yang hanya tahu apa yang bisa diobservasi
sam

8
Saya memiliki wawancara Angular 4 pada hari Rabu. Karena saya masih mempelajari platform baru, dia membuat saya tersandung dengan menanyakan sesuatu seperti "Apa yang akan terjadi jika saya berlangganan sebuah observable yang ada dalam modul yang belum dimuat dengan malas?" Saya tidak yakin, tetapi dia mengatakan kepada saya bahwa jawabannya adalah menggunakan BSubject - PERSIS bagaimana Pak Bhadoria menjelaskannya di atas. Jawabannya adalah menggunakan BSubject karena selalu mengembalikan nilai terbaru (setidaknya itulah yang saya ingat komentar terakhir pewawancara itu).
bob.mazzo

1
@ bob.mazzo Mengapa saya perlu menggunakan BSubject untuk kasing itu? - Jika saya berlangganan Pengamat saya tidak akan menerima apa-apa karena pengamat belum diinisialisasi sehingga tidak dapat mendorong data ke pengamat dan Jika saya menggunakan subjek BS saya tidak akan menerima apa pun karena alasan yang sama. Dalam kedua kasus, pelanggan tidak akan menerima apa pun karena berada dalam modul yang belum diinisialisasi. Apakah saya benar?
Rafael Reyes

183

Diamati: Hasil berbeda untuk setiap Pengamat

Satu perbedaan yang sangat penting. Karena Observable hanyalah sebuah fungsi, ia tidak memiliki status apa pun, jadi untuk setiap Pengamat baru, ia menjalankan kode buat yang dapat diamati berulang kali. Ini menghasilkan:

Kode dijalankan untuk setiap pengamat. Jika ini panggilan HTTP, ia dipanggil untuk setiap pengamat

Ini menyebabkan bug utama dan inefisiensi

BehaviorSubject (atau Subject) menyimpan detail pengamat, menjalankan kode hanya sekali dan memberikan hasilnya kepada semua pengamat.

Ex:

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>

Keluaran:

"observer 1: 0.7184075243594013"
"observer 2: 0.41271850211336103"
"observer subject 1: 0.8034263165479893"
"observer subject 2: 0.8034263165479893"

Amati bagaimana menggunakan Observable.createoutput yang dibuat berbeda untuk setiap pengamat, tetapi BehaviorSubjectmemberikan hasil yang sama untuk semua pengamat. Ini penting.


Perbedaan lainnya dirangkum.

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃         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
siapa pun yang datang dari KnockoutJS's ko.observable()akan segera melihat lebih banyak persamaan Rx.BehaviorSubjectdibandingkan denganRx.Observable
Simon_Weaver

@Skeptor Observable: metode berlangganan akan selalu memicu metode onNext yang terkait dengan pengamat dan membawa nilai kembali. BehaviourSubject / Subject: Akan selalu mengembalikan nilai terbaru di aliran. di sini berlangganan metode dengan subjek tidak akan memicu metode onNext dari Pengamat sampai menemukan nilai terbaru dalam aliran.
Mohan Ram

62

Dapat diamati dan subjek keduanya adalah cara yang dapat diamati yang dapat diamati oleh pengamat. tetapi keduanya memiliki beberapa karakteristik unik. Selanjutnya ada total 3 jenis mata pelajaran yang masing-masing lagi memiliki karakteristik unik. mari kita coba untuk memahami masing-masing dari mereka.

Anda dapat menemukan contoh praktisnya di sini di stackblitz . (Anda perlu memeriksa konsol untuk melihat output aktual)

masukkan deskripsi gambar di sini

Observables

Mereka dingin: Kode dijalankan ketika mereka memiliki setidaknya satu pengamat.

Membuat salinan data: Dapat diamati membuat salinan data untuk setiap pengamat.

Uni-directional: Pengamat tidak dapat menetapkan nilai untuk diamati (asal / master).

Subject

Mereka panas: kode dieksekusi dan nilai disiarkan meskipun tidak ada pengamat.

Membagikan data: Data yang sama dibagikan di antara semua pengamat.

bi-directional: Pengamat dapat memberikan nilai untuk diamati (asal / master).

Jika menggunakan subjek maka Anda melewatkan semua nilai yang disiarkan sebelum pembuatan pengamat. Jadi inilah Subyek Putar Ulang

ReplaySubject

Mereka panas: kode dieksekusi dan nilai disiarkan meskipun tidak ada pengamat.

Membagikan data: Data yang sama dibagikan di antara semua pengamat.

bi-directional: Pengamat dapat memberikan nilai untuk diamati (asal / master). plus

Putar ulang aliran pesan: Tidak masalah ketika Anda berlangganan subjek replay, Anda akan menerima semua pesan yang disiarkan.

Dalam subjek dan subjek replay Anda tidak dapat mengatur nilai awal untuk diamati. Jadi inilah Subyek Perilaku

BehaviorSubject

Mereka panas: kode dieksekusi dan nilai disiarkan meskipun tidak ada pengamat.

Membagikan data: Data yang sama dibagikan di antara semua pengamat.

bi-directional: Pengamat dapat memberikan nilai untuk diamati (asal / master). plus

Putar ulang aliran pesan: Tidak masalah ketika Anda berlangganan subjek replay, Anda akan menerima semua pesan yang disiarkan.

Anda dapat menetapkan nilai awal: Anda dapat menginisialisasi nilai yang dapat diamati dengan nilai default.


3
Bisa bernilai menyebutkan bahwa a ReplaySubjectmemiliki sejarah dan dapat menyiarkan / memancarkan urutan nilai (lama). Hanya ketika buffer diatur ke 1 ia berperilaku mirip dengan a BehaviorSubject.
Wilt

28

Objek yang dapat diobservasi mewakili koleksi berbasis push.

Antarmuka Observer dan Observable menyediakan mekanisme umum untuk pemberitahuan berbasis push, juga dikenal sebagai pola desain pengamat. Objek yang Dapat Diobservasi mewakili objek yang mengirim pemberitahuan (penyedia); objek Observer mewakili kelas yang menerimanya (pengamat).

Kelas Subjek mewarisi Observable dan Observer, dalam arti bahwa itu adalah observer dan observable. Anda bisa menggunakan subjek untuk berlangganan semua pengamat, dan kemudian berlangganan subjek ke sumber data backend

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

Lebih lanjut tentang https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/subjects.md


apa perbedaan antara berlangganan.dispose () dan berlangganan.unsubscribe ()?
choopage - Jek Bao

4
@choopage tidak ada perbedaan. yang terakhir adalah cara baru
Royi Namir

Harus berhenti berlangganan sebelum subjek dibuang, jika tidak, berlangganan menjadi sampah karena berlangganan dengan nilai nol.
Sophie Zhang

20

Satu hal yang tidak saya lihat dalam contoh adalah ketika Anda menggunakan BehaviorSubject to Observable via asObservable, ia mewarisi perilaku mengembalikan nilai terakhir pada langganan.

Ini agak sulit, karena sering perpustakaan akan mengekspos bidang sebagai diamati (yaitu params di ActivatedRoute di Angular2), tetapi dapat menggunakan Subjek atau Perilaku Subjek di belakang layar. Apa yang mereka gunakan akan memengaruhi perilaku berlangganan.

Lihat di sini 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

Sebuah diamati memungkinkan Anda untuk berlangganan hanya sedangkan subjek memungkinkan Anda untuk kedua mempublikasikan dan berlangganan.

Jadi subjek memungkinkan layanan Anda digunakan sebagai penerbit dan pelanggan.

Sampai sekarang, saya tidak begitu pandai Observablesehingga saya hanya akan membagikan contoh Subject.

Mari kita mengerti lebih baik dengan contoh CLI Angular Jalankan perintah di bawah ini:

npm install -g @angular/cli

ng new angular2-subject

cd angular2-subject

ng serve

Ganti konten app.component.htmldengan:

<div *ngIf="message">
  {{message}}
</div>

<app-home>
</app-home>

Jalankan perintah ng g c components/homeuntuk menghasilkan komponen rumah. Ganti konten home.component.htmldengan:

<input type="text" placeholder="Enter message" #message>
<button type="button" (click)="setMessage(message)" >Send message</button>

#messageadalah variabel lokal di sini. Tambahkan properti message: string; ke kelas app.component.ts's.

Jalankan perintah ini ng g s service/message. Ini akan menghasilkan layanan di src\app\service\message.service.ts. Berikan layanan ini ke aplikasi .

Impor Subjectke MessageService. Tambahkan subjek juga. Kode akhir akan terlihat seperti ini:

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

Sekarang, suntikkan layanan ini home.component.tsdan berikan instance ke konstruktor. Lakukan ini app.component.tsjuga. Gunakan contoh layanan ini untuk meneruskan nilai #messageke fungsi layanan 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);
  }
}

Di dalam app.component.ts, berlangganan dan berhenti berlangganan (untuk mencegah kebocoran memori) ke 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();
  }
}

Itu dia.

Sekarang, setiap nilai masuk dalam #messagedari home.component.htmlakan dicetak ke {{message}}dalamapp.component.html


Mengapa gambar raksasa? Jika itu tidak terkait langsung dengan jawaban Anda, sepertinya votebait.
ruffin

@ruffin Ini hanya jawaban rata-rata dengan jumlah suara rata-rata, lihat profil saya. Tidak pasti votebait: D
xameeramir

1
Saya memberi Anda upvote sebelumnya, tetapi Anda menghindari pertanyaan mengapa gambar itu ada di sana. Ini tidak terkait langsung dengan jawaban Anda. Tidak masalah jika Anda memiliki banyak perwakilan atau tidak - jika gambar tidak secara langsung dan khusus dijelaskan, saya akan meminta Anda menghapusnya . /
shrug

1
@ruffin Jika itu bertentangan dengan persetujuan komunitas, maka itu tidak seharusnya ada di sana!
xameeramir

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 memiliki pengamat dan dapat diobservasi, Rxjs menawarkan beberapa kelas untuk digunakan dengan aliran data, dan salah satunya adalah BehaviorSubject.

Observable : Observable adalah koleksi malas dari berbagai nilai dari waktu ke waktu.

BehaviorSubject : Subjek yang membutuhkan nilai awal dan memancarkan nilainya saat ini untuk pelanggan baru.

 // 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

Pikirkan Observable sebagai pipa dengan air yang mengalir di dalamnya, kadang-kadang air mengalir dan terkadang tidak. Dalam beberapa kasus, Anda mungkin benar-benar membutuhkan pipa yang selalu berisi air, Anda dapat melakukannya dengan membuat pipa khusus yang selalu mengandung air, sekecil apa pun itu, sebut saja pipa khusus ini , BehaviorSubject , jika Anda kebetulan penyedia pasokan air di komunitas Anda, Anda dapat tidur nyenyak di malam hari mengetahui bahwa pipa yang baru Anda pasang berfungsi.

Dalam istilah teknis: Anda dapat menemukan kasus penggunaan di mana Observable harus selalu memiliki nilai di dalamnya, mungkin Anda ingin menangkap nilai teks input dari waktu ke waktu, Anda kemudian dapat membuat instance BehaviorSubject untuk memastikan perilaku semacam ini, katakanlah:


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

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

Anda kemudian dapat menggunakan "nilai" untuk sampel perubahan dari waktu ke waktu.


firstNameChanges.value;

Ini berguna ketika Anda menggabungkan Observables nanti, dengan melihat jenis aliran Anda sebagai BehaviorSubject Anda kemudian dapat memastikan bahwa aliran setidaknya menyala atau sinyal sekali saja minimal .

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.