BehaviorSubject vs Observable?

Kev*_*ark 591 rxjs behaviorsubject angular

我正在研究Angular RxJs模式,我不明白a BehaviorSubject和a之间的区别Observable.

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

你什么时候使用Observablevs BehaviorSubject?使用BehaviorSubjectover Observable或反之亦然有好处吗?

Sha*_*ria 852

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
Run Code Online (Sandbox Code Playgroud)

具有常规主题的示例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
Run Code Online (Sandbox Code Playgroud)

可以从两者SubjectBehaviorSubject使用创建一个observable subject.asObservable().

唯一的区别是您无法将值发送到可观察的使用next()方法.

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

  • @jmod999第二个示例是一个常规主题,它在调用订阅之前接收一个值.在常规主题中,仅在调用订阅后接收的值触发订阅.由于a是在订阅之前收到的,因此不会将其发送到订阅. (17认同)
  • 我星期三接受了Angular 4的采访.因为我还在学习新的平台,所以他惹我生气,问我"如果我订阅了一个尚未延迟加载的模块中的可观察量会发生什么?" 我不确定,但他告诉我答案是使用BSubject - 确切地说Bhadoria先生如何解释它.答案是使用BSubject因为它总是返回最新值(至少我记得面试官对此的最终评论). (7认同)
  • 我对常规科目的例子2有点困惑.为什么订阅在第二行上没有得到任何东西你使用subject.next("b")向主题发送值? (6认同)
  • @bob.mazzo 为什么我需要在这种情况下使用 BSubject?-- 如果我订阅那个观察者,我不会收到任何东西,因为观察者没有被初始化,所以它不能将数据推送给观察者,如果我使用 BSubject,我也不会因为同样的原因收到任何东西。在这两种情况下,订阅者都不会收到任何东西,因为它位于尚未初始化的模块中。我对吗? (4认同)

Vam*_*shi 149

可观察:每个观察者的结果不同

一个非常重要的区别.由于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));
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.3/Rx.min.js"></script>
Run Code Online (Sandbox Code Playgroud)

输出:

"observer 1: 0.7184075243594013"
"observer 2: 0.41271850211336103"
"observer subject 1: 0.8034263165479893"
"observer subject 2: 0.8034263165479893"
Run Code Online (Sandbox Code Playgroud)

观察如何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                          ?
?????????????????????????????????????????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)

  • 与`Rx.Observable`相比,来自`KnockoutJS's ko.observable()`的任何人都将立即看到与`Rx.BehaviorSubject`更多的相似之处。 (3认同)

Ked*_*444 38

观察到的主题都是可观察的指观察者可以跟踪他们.但它们都有一些独特的特征.此外,总共有3种类型的受试者,每种受试者都具有独特的特征.让我们试着去了解每一个.

你可以在这里找到stackblitz的实际例子. (您需要检查控制台以查看实际输出)

在此输入图像描述

Observables

它们很冷:代码在列表中执行,它们只有一个观察者.

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

单向:观察者无法为可观察(原点/主)赋值.

Subject

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

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

双向: Observer可以为可观察的值(origin/master)赋值.

如果正在使用主题,那么您将错过在创建观察者之前所有的值.所以这里有重播主题

ReplaySubject

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

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

双向: Observer可以为可观察的值(origin/master)赋值.加

重播消息流:无论您何时订阅重播主题,您都将收到所有brodcasted消息.

在主题和重放主题中,您无法将初始值设置为可观察.所以这里有行为主题

BehaviorSubject

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

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

双向: Observer可以为可观察的值(origin/master)赋值.加

重播消息流:无论您何时订阅重播主题,您都将收到所有brodcasted消息.

您可以设置初始值:您可以使用默认值初始化observable.

  • 值得一提的是,“ReplaySubject”具有历史记录,并且可以广播/发出一系列(旧)值。仅当 buffer 设置为 1 时,它的行为类似于“BehaviorSubject”。 (4认同)
  • 对于BehaviorSubject,“重播消息流”段落似乎不正确 (3认同)

Md *_*ker 26

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

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

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

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();
Run Code Online (Sandbox Code Playgroud)

有关https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/subjects.md的更多信息

  • @choopage没有区别。后者是新方法 (3认同)

小智 17

我在示例中没有看到的一件事是,当您通过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);
Run Code Online (Sandbox Code Playgroud)


xam*_*mir 10

一个可观察的允许而您只订阅主题,您可以发布和订阅.

因此,主题允许您的服务既可以用作发布者,也可以用作订阅者.

截至目前,我不是很擅长,Observable所以我只会分享一个例子Subject.

让我们通过Angular CLI示例更好地理解.运行以下命令:

npm install -g @angular/cli

ng new angular2-subject

cd angular2-subject

ng serve
Run Code Online (Sandbox Code Playgroud)

替换为的内容app.component.html:

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

<app-home>
</app-home>
Run Code Online (Sandbox Code Playgroud)

运行该命令ng g c components/home以生成主组件.替换为的内容home.component.html:

<input type="text" placeholder="Enter message" #message>
<button type="button" (click)="setMessage(message)" >Send message</button>
Run Code Online (Sandbox Code Playgroud)

#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
  }
}
Run Code Online (Sandbox Code Playgroud)

现在,将此服务注入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);
  }
}
Run Code Online (Sandbox Code Playgroud)

在里面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();
  }
}
Run Code Online (Sandbox Code Playgroud)

而已.

现在,任何值内进入#messagehome.component.html,应打印到{{message}}app.component.html

  • 我早些时候给了你一个赞成票,但你已经回避了为什么图像在那里的问题。这与您的答案没有直接关系。如果您有很多代表并不重要 - [如果图像不是直接且特别说明性的,我会要求您将其删除](https://meta.stackexchange.com/a/ 9399/184684)。/耸肩 (2认同)

小智 10

应用程序组件.ts

behaviourService.setName("behaviour");
Run Code Online (Sandbox Code Playgroud)

行为.service.ts

private name = new BehaviorSubject("");
getName = this.name.asObservable();

constructor() {}

setName(data) {
    this.name.next(data);
}
Run Code Online (Sandbox Code Playgroud)

自定义组件.ts

behaviourService.subscribe(response=>{
    console.log(response);    //output: behaviour
});
Run Code Online (Sandbox Code Playgroud)


小智 6

BehaviorSubject vs Observable:RxJS 有观察者和可观察者,Rxjs 提供了多个与数据流一起使用的类,其中之一是BehaviorSubject。

Observables:Observables 是随时间变化的多个值的惰性集合。

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

 // 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
Run Code Online (Sandbox Code Playgroud)


小智 6

Observables想象成一根管子,里面有流动的水,有时水会流动,有时不会。在某些情况下,您实际上可能需要一个总是有水的管道,您可以通过创建一个始终包含水的特殊管道来做到这一点,无论它有多小,让我们称之为特殊管道BehaviorSubject,如果您碰巧是如果您所在社区的供水供应商,您可以在晚上安然入睡,因为您知道新安装的管道可以正常工作。

在技​​术术语中:您可能会遇到 Observable 应该始终具有价值的用例,也许您想随着时间的推移捕获输入文本的值,然后您可以创建 BehaviorSubject 的实例来确保这种行为,让我们说:


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

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

Run Code Online (Sandbox Code Playgroud)

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


firstNameChanges.value;

Run Code Online (Sandbox Code Playgroud)

当您稍后结合 Observables 时,这会很方便,通过将流的类型视为 BehaviorSubject,您可以确保流至少触发或仅发出一次至少信号

  • 涵盖了很多部分,但你的解释的光明的一面是给出了一个易于理解的类比,Kudo! (2认同)

Nae*_*hir 5

Observable 是一个泛型,

可观察量是随时间变化的多个值的惰性集合。

只是一个函数,没有状态

为每个观察者运行代码

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

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

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

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

BehaviourSubject 的独特功能如下:

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

订阅后,它返回主题的最后一个值。常规可观察对象仅在收到onnext时触发

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


Yil*_*maz 5

rxjs 中的主题本质上是一个观察者和可观察的混合体。观察者是我们输入值的东西,可观察者是我们可以观察值的东西。

  • 主题默认为热门。默认情况下,可观察量是冷的。这意味着在有人订阅之前它们不会发出任何值。当我们创建一个主题时,我们可以从中发出一个值,即使尚未有人订阅该值,该值也会被发出。
  • 默认情况下,主题是多播的。默认情况下,Observable 是单播的,这意味着对于我们拥有的每个不同的观察者,我们必须订阅一个 Observable,如果该 Observable 发出一个值,则该值将针对每个订阅者一次流经管道内的所有不同运算符。多播意味着所有其他运算符都将为每个值运行一次,无论我们有多少观察者。
  • 注意:主题是多播的,但是如果您将管道语句链接到它,那么将返回一个新的冷且单播的可观察量。

行为主体与主体相同,但也采用初始“种子”值。新订阅者立即获得最新的价值。如果有人订阅了行为主题,它将立即收到最新的值。因此,行为主体总是会向订阅者提供一些价值。

关于行为主体最有用的事情是当我们开始发出网络请求时。想象一下,我们将一些管道内容链接到行为主体,并在管道函数或管道运算符内,我们最终发出网络请求并获取一些数据。您最终可能希望有其他东西订阅该可观察对象并立即获取已获取的数据。使用行为主体,我们可以轻松地实现这种行为。