BehaviorSubject vs Observable?

时间:2016-09-14 15:14:44

标签: angular rxjs behaviorsubject

我正在研究Angular RxJs模式,我不明白BehaviorSubjectObservable之间的区别。

根据我的理解,BehaviorSubject是一个可以随时间变化的值(可以订阅,订阅者可以接收更新的结果)。这似乎与Observable的目的完全相同。

您何时使用ObservableBehaviorSubject?使用BehaviorSubject优于Observable还是反之亦然?

11 个答案:

答案 0 :(得分:750)

BehaviorSubject 是一种主题,主题是一种特殊类型的可观察对象,因此您可以像任何其他可观察对象一样订阅消息。 BehaviorSubject的独特功能是:

  • 它需要一个初始值,因为它必须始终返回订阅值,即使它没有收到next()
  • 订阅后,它会返回主题的最后一个值。常规observable仅在收到onnext
  • 时触发
  • 在任何时候,您都可以使用getValue()方法在不可观察的代码中检索主题的最后一个值。

与可观察对象相比,主题的独特特征是:

  • 除了作为观察者之外,它还是一个观察者,因此除了订阅它之外,您还可以向主题发送值。

此外,您可以使用asObservable()上的BehaviorSubject方法从行为主题中获取观察结果。

Observable 是Generic,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

可以使用SubjectBehaviorSubjectsubject.asObservable()创建可观察对象。

唯一的区别是您无法使用next()方法将值发送到observable。

在Angular服务中,我会将BehaviorSubject用于数据服务,因为角度服务通常在组件和行为主体之前初始化,以确保消费服务的组件接收最后更新的数据,即使自那时没有新的更新组件对此数据的订阅。

答案 1 :(得分:126)

Observable:每个Observer的结果不同

一个非常重要的区别。由于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>
&#13;
&#13;
&#13;

输出:

"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                          │
└─────────────────────────────────────┴─────────────────────────────────────┘

答案 2 :(得分:25)

Observable对象表示基于推送的集合。

Observer和Observable接口为基于推送的通知提供了一种通用机制,也称为观察者设计模式。 Observable对象表示发送通知的对象(提供者); Observer对象表示接收它们的类(观察者)。

Subject类继承Observable和Observer,因为它既是观察者又是observable。您可以使用主题订阅所有观察者,然后将主题订阅到后端数据源

SELECT CoinAwards.EmployeeID, CoinAwards.AwardDate, CoinAwards.CoinLevel,
  SUM(CoinLevels.Value) AS Sum_Of_2015_Coin_Values, 
  Employees.FirstName, Employees.Lastname
FROM CoinAwards 
LEFT OUTER JOIN
  CoinLevels ON CoinAwards.CoinLevel = CoinLevels.ID 
LEFT OUTER JOIN
  Employees ON Employees.EmployeeID = CoinAwards.EmployeeID
WHERE  (CoinAwards.AwardDate BETWEEN '01/01/2015' AND '12/31/2015')
GROUP BY CoinAwards.EmployeeID

更多关于https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/subjects.md

答案 3 :(得分:15)

我在示例中没有看到的一件事是,当您通过asObservable将BehaviorSubject强制转换为Observable时,它会继承在订阅时返回最后一个值的行为。

这是一个棘手的问题,因为库通常会将字段显示为可观察的(即Angular2中的ActivatedRoute中的参数),但可以在幕后使用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);

答案 4 :(得分:15)

可观察主题都是可观察的,观察者可以跟踪它们。但是它们都有一些独特的特征。此外,共有3种主题,每个主题又具有独特的特征。让我们尝试了解它们中的每一个。

您可以在stackblitz上找到实际示例。 (您需要检查控制台以查看实际输出)

enter image description here

Observables

它们很冷: 代码在具有单个观察者的列表上执行。

创建数据副本: Observable为每个观察者创建数据副本。

单向: 观察者无法将值分配给可观察(原始/主)。

Subject

它们很热门: 即使没有观察者,代码也会被执行并传播价值。

共享数据: 所有观察者之间共享相同的数据。

双向: 观察者可以将值分配给可观察(原始/主)。

如果正在使用主题,则您会错过创建观察者之前广播的所有值。因此出现了重播主题

Replay Subject

它们很热门: 即使没有观察者,代码也会被执行并传播价值。

共享数据: 所有观察者之间共享相同的数据。

双向: 观察者可以将值分配给可观察者(来源/主数据)。加

重播消息流: :无论何时订阅重播主题,您都会收到所有广播消息。

在主题和重播主题中,您无法将初始值设置为可观察。因此,行为主题

Behavioral Subject

它们很热门: 即使没有观察者,代码也会被执行并传播价值。

共享数据: 所有观察者之间共享相同的数据。

双向: 观察者可以将值分配给可观察者(来源/主数据)。加

重播消息流: :无论何时订阅重播主题,您都会收到所有广播消息。

您可以设置初始值: 。您可以使用默认值初始化可观察对象。

答案 5 :(得分:9)

observable仅允许您订阅,而subject允许您同时发布和订阅。

因此,主题允许您的services同时用作发布者和订阅者。

截至目前,我对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生成服务。提供this service to the app

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的{​​{1}}内输入的任何值都应打印在home.component.html内的{{message}}

答案 6 :(得分:2)

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

答案 7 :(得分:1)

Observables (可观察的物体)视为其中装有流动水的管道,有时水会流动,有时却不会。在某些情况下,您实际上可能需要一个总是有水的管道,您可以通过创建一个特殊的管道来做到这一点,不管它有多小,总有一个水,让我们称此特殊管道 BehaviorSubject >,如果您碰巧是社区中的供水服务提供商,则可以在得知新安装的管道可以正常工作的情况下在晚上安然入睡。

从技术上讲:您可能会遇到一些用例,在这些用例中,一个Observable总是应该有值,也许您想随着时间的推移捕获输入文本的值,然后可以创建BehaviorSubject实例来确保这种行为,可以说:


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

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

然后您可以使用“值”来抽样随时间变化。


firstNameChanges.value;

稍后结合使用Observables时,这很方便,通过查看流的类型为BehaviorSubject,可以确保流至少触发或发出至少一次信号

答案 8 :(得分:0)

BehaviorSubject Observable :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

答案 9 :(得分:0)

行为主题

BehaviorSubject建立在与ReplaySubject相同的功能之上,像主题一样热门,并重播以前的值。

BehaviorSubject增加了一项功能,您可以为BehaviorSubject赋予一个初始值。让我们继续看一下该代码

import { ReplaySubject } from 'rxjs';

const behaviorSubject = new BehaviorSubject(
  'hello initial value from BehaviorSubject'
);

behaviorSubject.subscribe(v => console.log(v));

behaviorSubject.next('hello again from BehaviorSubject');

可观察物

首先,我们将研究创建常规Observable的最小API。有两种创建Observable的方法。我们创建Observable的方法是实例化该类。其他运算符可以简化此操作,但是我们希望将实例化步骤与我们不同的Observable类型进行比较

import { Observable } from 'rxjs';

const observable = new Observable(observer => {
  setTimeout(() => observer.next('hello from Observable!'), 1000);
});

observable.subscribe(v => console.log(v));

答案 10 :(得分:0)

可观察 是泛型,

Observable 是随时间推移的多个值的惰性集合。

只是一个函数,没有状态

为每个观察者运行代码

行为主题: 需要初始值并将其当前值发送给新订阅者的主题。

从技术上讲是 Observable 的子类型,因为 BehaviorSubject 是具有特定品质的 observable。

有状态。在内存中存储数据

相同的代码只对所有观察者运行一次

BehaviorSubject 的独特之处如下:

它需要一个初始值,因为它必须始终在订阅时返回一个值,即使它没有收到 next()

订阅后,它返回主题的最后一个值。常规 observable 仅在收到 onnext

时触发

在任何时候,您都可以使用 getValue() 方法在不可观察代码中检索主题的最后一个值。