BehaviorSubjectとObservable?


690

Angular RxJsパターンを調べていますが、a BehaviorSubjectとの違いがわかりませんObservable

私の理解では、a BehaviorSubjectは時間とともに変化する可能性がある値です(サブスクライブでき、サブスクライバーは更新された結果を受け取ることができます)。これはとまったく同じ目的のようObservableです。

いつObservablevs を使用しBehaviorSubjectますか?BehaviorSubject以上を使用するメリットはありますObservableか、またはその逆ですか?

回答:


968

BehaviorSubjectはサブジェクトの一種であり、サブジェクトは特別なタイプのオブザーバブルなので、他のオブザーバブルと同様にメッセージをサブスクライブできます。BehaviorSubjectの独自の機能は次のとおりです。

  • 受け取っていない場合でもサブスクリプションの値を常に返す必要があるため、初期値が必要です。 next()
  • サブスクリプション時に、サブジェクトの最後の値を返します。通常のオブザーバブルは、それを受け取ったときにのみトリガーされますonnext
  • メソッドを使用して、任意の時点で、サブジェクトの最後の値を監視不能コードで取得できますgetValue()

オブザーバブルと比較したサブジェクトの固有の特徴は次のとおりです。

  • これはオブザーバブルであることに加えてオブザーバーであるため、サブスクライブに加えてサブジェクトに値を送信することもできます。

さらに、のasObservable()メソッドを使用して、行動主体からオブザーバブルを取得できますBehaviorSubject

ObservableはジェネリックでありBehaviorSubject、BehaviorSubjectは特定の品質のオブザーバブルであるため、技術的にはObservableのサブタイプです。

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

観測可能で、両方から作成することが可能SubjectBehaviorSubject使用しますsubject.asObservable()

唯一の違いは、observable using next()メソッドに値を送信できないことです。

Angularサービスでは、BehaviorSubjectコンポーネントと動作サブジェクトがサービスのコンポーネントがこのデータへのサブスクリプション以降の新しい更新がない場合でも、最後に更新されたデータを確実に受信する前に、Angleサービスが初期化されることが多いため、データサービスに使用します。


7
通常の主題の例2とは少し混乱しています。サブスクリプションが、subject.next( "b")を使用してsubjectに値を送信する2行目にも何も得ないのはなぜですか?
jmod999 2016

25
@ jmod999 2番目の例は、サブスクリプションが呼び出される直前に値を受け取る通常のサブジェクトです。通常のサブジェクトでは、サブスクリプションは、サブスクリプションが呼び出された後に受信された値に対してのみトリガーされます。はサブスクリプションの直前に受信されるため、サブスクリプションには送信されません。
Shantanu Bhadoria

その素晴らしい解決策についてのメモ、それを関数で使用して返す場合は、オブザーバブルを返します。サブジェクトを返す際にいくつかの問題があり、Observablesだけを知っている他の開発者を混乱させます
sam

8
私は水曜日にAngular 4のインタビューを行いました。私はまだ新しいプラットフォームを学習しているので、「レイジーロードされていないモジュールにあるオブザーバブルをサブスクライブするとどうなりますか?」私は確信が持てませんでしたが、彼は答えはBSubjectを使用することだと言った-まさにバドリア氏が上でそれを説明した方法。答えはBSubjectを使用することでした。BSubjectは常に最新の値を返すためです(少なくとも、それがインタビュアーの最後のコメントを覚えている方法です)。
bob.mazzo 2017年

1
@ bob.mazzoその場合、なぜBSubjectを使用する必要があるのですか?-そのオブザーバーをサブスクライブした場合、オブザーバーが初期化されていないため何も受信しません。オブザーバーにデータをプッシュできません。また、BSubjectを使用すると、同じ理由で何も受信しません。どちらの場合も、初期化されていないモジュール内にあるため、サブスクライバーは何も受信しません。私は正しいですか?
Rafael Reyes

183

観察可能:観察者ごとに異なる結果

1つの非常に重要な違い。Observableは単なる関数なので、状態はありません。そのため、新しいObserverごとに、observable createコードを繰り返し実行します。これは結果として:

コードはオブザーバーごとに実行されます。HTTP呼び出しの場合、オブザーバーごとに呼び出されます

これは大きなバグと非効率を引​​き起こします

BehaviorSubject(またはSubject)は、オブザーバーの詳細を格納し、コードを1回だけ実行して、その結果をすべてのオブザーバーに提供します。

例:

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
でしょ

@Skeptor Observable: subscribeメソッドは常に、オブザーバーに関連付けられたonNextメソッドをトリガーし、戻り値をもたらします。BehaviourSubject / Subject:常にストリームの最新の値を返します。ここで、サブジェクトを持つサブスクライブメソッドは、ストリーム内で最新の値が見つかるまで、オブザーバーのonNextメソッドをトリガーしません。
Mohan Ram

62

観察可能対象の両方が観察可能であることは、観察者がそれらを追跡できることを意味します。しかし、どちらにもいくつかのユニークな特徴があります。さらに、合計3種類のサブジェクトがあり、それぞれに固有の特性があります。それぞれを理解してみましょう。

この実用的な例は、こちらのstackblitzにあります。 (実際の出力を確認するには、コンソールを確認する必要があります)

ここに画像の説明を入力してください

Observables

彼らは寒いです。少なくとも1人のオブザーバーがいるときにコードが実行されます。

データのコピーを作成: Observableは、各オブザーバーのデータのコピーを作成します。

単方向:オブザーバーは、observable(origin / master)に値を割り当てることができません。

Subject

ホットです。オブザーバーがいない場合でも、コードが実行され、値がブロードキャストされます。

データの共有すべてのオブザーバー間で同じデータが共有されます。

双方向:オブザーバーは、observable(origin / master)に値を割り当てることができます。

サブジェクトを使用している場合、オブザーバーの作成前にブロードキャストされるすべての値を見逃します。だからここにリプレイ件名が来ます

ReplaySubject

ホットです。オブザーバーがいない場合でも、コードが実行され、値がブロードキャストされます。

データの共有すべてのオブザーバー間で同じデータが共有されます。

双方向:オブザーバーは、observable(origin / master)に値を割り当てることができます。プラス

メッセージストリームの再生:再生サブジェクトをサブスクライブすると、ブロードキャストされたすべてのメッセージを受信します。

サブジェクトとリプレイサブジェクトでは、初期値を観測可能に設定することはできません。だからここに行動の主題があります

BehaviorSubject

ホットです。オブザーバーがいない場合でも、コードが実行され、値がブロードキャストされます。

データの共有すべてのオブザーバー間で同じデータが共有されます。

双方向:オブザーバーは、observable(origin / master)に値を割り当てることができます。プラス

メッセージストリームの再生:再生サブジェクトをサブスクライブすると、ブロードキャストされたすべてのメッセージを受信します。

初期値を設定できます:デフォルト値でオブザーバブルを初期化できます。


3
a ReplaySubjectには履歴があり、一連の(古い)値をブロードキャストまたは送信できることに言及する価値があります。bufferが1に設定されている場合のみ、と同様に動作しBehaviorSubjectます。
ウィルト

28

Observableオブジェクトは、プッシュベースのコレクションを表します。

ObserverおよびObservableインターフェースは、オブザーバーデザインパターンとも呼ばれる、プッシュベースの通知のための一般化されたメカニズムを提供します。Observableオブジェクトは、通知を送信するオブジェクト(プロバイダー)を表します。Observerオブジェクトは、それらを受け取るクラス(オブザーバー)を表します。

Subjectクラスは、ObserverとObserverの両方であるという意味で、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-Jek Bao 2017年

4
@choopage違いはありません。後者は新しい方法です
Royi Namir

サブジェクトが破棄される前にサブスクライブを解除する必要があります。それ以外の場合は、null値をサブスクライブするため、サブスクリプションはガベージになります。
Sophie Zhang

20

例に見られないのは、asObservableを介してBehaviorSubjectをObservableにキャストすると、サブスクリプションで最後の値を返す動作を継承することです。

多くの場合、ライブラリはフィールドを観察可能として公開するため(つまり、Angular2のActivatedRouteのparams)、これは難しいですが、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ます。このサービスをアプリに提供します

にインポートSubjectMessageServiceます。件名も追加します。最終的なコードは次のようになります。

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
xameeramir

1
先ほど賛成票を差し上げましたが、なぜ画像がそこにあるのかという疑問を避けました。それはあなたの答えとは直接関係ありません。多くの担当者がいてもいなくてもかまいません。画像が直接かつ具体的に説明されていない場合は、削除するようにお願いします。/
shrug

1
@ruffinそれがコミュニティの同意に反する場合、それは確かにそこにあるべきではありません!
xameeramir 2018年

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

BehaviorSubjectObservable:RxJSにはオブザーバーとオブザーバブルがあり、Rxjsはデータストリームで使用する複数のクラスを提供し、そのうちの1つは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

Observablesは、水が流れるパイプと考えてください。水が流れる場合と流れない場合があります。いくつかのケースでは、あなたが実際にそれで常に水を持っている配管を必要とするかもしれない、あなたは常に関係なく、それがどのように小さな水が含まれていない特殊なパイプを作成することによってこれを行うことができ、この特別なパイプを呼び出すことができますBehaviorSubjectをあなたがあることを起こる場合は、あなたのコミュニティの給水プロバイダーであれば、新しく設置したパイプが機能することを知っていれば、夜は安らかに眠ることができます。

技術的に言えば、Observableに常に値が必要なユースケースに遭遇する可能性があります。おそらく、入力テキストの値を経時的に取得したい場合は、BehaviorSubjectのインスタンスを作成して、この種の動作を保証できます。


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

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

次に、「値」を使用して、時間の経過に伴う変化をサンプリングできます。


firstNameChanges.value;

これは、後でObservableを組み合わせるときに便利です。BehaviorSubjectとしてストリームのタイプを確認することで、ストリームが少なくとも1回だけ発火またはシグナル通知されるようにすることができます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.