我正在研究Angular RxJs模式,但我不了解BehaviorSubjectObservable之间的区别。

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

什么时候使用ObservableBehaviorSubject?与BehaviorSubject相比,使用Observable有好处吗?反之亦然?

#1 楼

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


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

订阅后,它返回主题的最后一个值。常规可观察对象仅在收到onnext时才会触发,您可以使用getValue()方法以不可观察的代码检索主题的最后一个值。

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


它不仅是可观察对象,而且是观察者,因此您除了订阅它之外还可以将值发送给对象。 br />
另外,您可以使用asObservable()上的BehaviorSubject方法从行为主体中获得可观察到的信息。

Observable是泛型,而BehaviorSubject在技术上是Observable的子类型,因为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
 


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

唯一的区别是您无法使用next()方法将值发送给可观察对象。

在Angular服务中,我经常将BehaviorSubject用于数据服务作为角度服务在组件和行为主题之前进行初始化,以确保使用该服务的组件可以接收到最后更新的数据,即使自该组件订阅该数据以来没有新的更新也如此。

评论


我对常规主题的示例2有点困惑。为什么在您使用subject.next(“ b”)将值发送给主题的第二行上,订阅甚至无法获得任何结果?

– jmod999
16年11月11日在17:45

@ jmod999第二个示例是一个常规主题,该主题在调用订阅之前立即接收值。在常规主题中,仅针对调用后收到的值触发订阅。由于在订阅之前就已收到a,因此不会将其发送到订阅。

– Shantannu Bhadoria
17年4月19日在19:20

关于该奇妙解决方案的注释,如果在函数中使用它并返回它,则返回一个可观察的。我在返回主题时遇到了一些问题,这使其他只知道什么是Observable的开发人员感到困惑

–山姆
17年6月15日在13:41

我在星期三接受了Angular 4采访。由于我仍在学习新平台,因此他向我提出以下问题,例如“如果我订阅了一个尚未延迟加载的模块中的可观察对象,将会发生什么?”,这使我震惊。我不确定,但是他告诉我答案是使用BSubject-完全是Bhadoria先生在上面的解释。答案是使用BSubject,因为它总是返回最新值(至少这是我记得访问者对此的最终评论的方式)。

– bob.mazzo
17年11月24日15:48



@ bob.mazzo为什么在这种情况下需要使用BSubject? -如果我订阅该观察者,由于观察者尚未初始化,因此不会收到任何信息,因此它无法将数据推送给观察者;如果使用BSubject,由于相同的原因,我也不会获得任何信息。在这两种情况下,订户都不会收到任何东西,因为它位于尚未初始化的模块中。我对吗?

–拉斐尔·雷耶斯(Rafael Reyes)
18年6月13日在23:03

#2 楼

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

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





输出:

"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                          ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛


评论


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

–Simon_Weaver
17年6月21日在21:57

@Skeptor Observable:subscribe方法将始终触发与观察者关联的onNext方法并带来返回值。 BehaviourSubject / Subject:将始终返回流中的最新值。在此,带有主题的订阅方法将不会触发其Observer的onNext方法,直到它在流中找到最新值为止。

– Mohan Ram
17-10-26在10:58



#3 楼

可观察者和对象都是可观察者,意味着观察者可以跟踪它们。但是它们都有一些独特的特征。此外,共有3种类型的主题,每个主题又具有独特的特征。让我们尝试理解它们中的每一个。

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



Observables

它们很冷:代码被执行当他们至少有一个观察者时。

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

单向:观察者无法将值分配给observable (来源/主数据)。

Subject

它们很热:即使没有观察者,代码也将被执行且价值得到传播。

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

双向:观察者可以将值分配给observable(来源/主对象)。

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

ReplaySubject

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

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

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

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

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

BehaviorSubject

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

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

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

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

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

评论


值得一提的是,ReplaySubject具有历史记录,并且可以广播/发射一系列(旧)值。仅当buffer设置为1时,它的行为才类似于BehaviorSubject。

–狂野
19年11月19日在8:29

对于BehaviorSubject,“重放消息流”段落似乎不正确

– Jaqen H'ghar
12月19日7:48

#4 楼

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

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

Subject类既继承了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-的更多信息扩展程序/RxJS/blob/master/doc/gettingstarted/subjects.md

评论


subscription.dispose()和subscription.unsubscribe()有什么区别?

–选择页-杰克·鲍(Jek Bao)
17年4月20日在8:19

@choopage没有区别。后者是新方法

–罗伊·纳米尔(Royi Namir)
17年4月25日在19:06

应该在处理主题之前取消订阅,否则,订阅将成为垃圾,因为它订阅了空值。

–张柔(Sophie Zhang)
18-10-11在14:28

#5 楼

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


#6 楼

可观察对象仅允许您订阅,而主题允许您同时发布和订阅。

因此主题允许您将服务同时用作发布者和订阅者。

到目前为止,我对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生成服务。向应用程序提供此服务。

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:在app.component.ts内,对Subject进行订阅和取消订阅(以防止内存泄漏):

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


就这样。

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

评论


为什么要大形象?如果它与您的答案没有直接关系,则看起来像是诱饵。

–松饼
18-10-2在14:19

@ruffin这只是平均投票数的平均答案,请看我的个人资料。不一定是::D

–赞美·安萨里(Zameer Ansari)
18-10-3在11:01

较早前,我曾给您提供过赞成票,但您却回避了为什么会出现图像的问题。它与您的答案没有直接关系。不管您是否有很多代表,都没关系-如果该图像不是直接和明确的说明,我将要求您删除它。 /耸肩

–松饼
18-10-3在20:18

@ruffin如果它违反了社区的同意,那么它肯定不应该在那里!

–赞美·安萨里(Zameer Ansari)
18-10-6在6:16

#7 楼

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


#8 楼

可以将Observables看作是其中有流动水的管道,有时水流动而有时却没有。在某些情况下,您实际上可能需要一个总是有水的管道,您可以通过创建一个特殊的管道来做到这一点,无论该管道有多小,该管道总是包含水,如果您碰巧是这样,可以称之为特殊管道BehaviorSubject。作为社区中的供水服务提供商,您可以在知道新安装的管道可以正常工作的情况下在晚上安然入睡。

从技术上讲:您可能会遇到一些用例,在该用例中,可观察对象应始终具有价值,也许您想随时间捕获输入文本的值,然后可以创建BehaviorSubject实例来确保这种行为,例如:

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

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

 


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

 
firstNameChanges.value;

 


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

#9 楼

BehaviorSubject与Observable:RxJS具有观察者和可观察对象,Rxjs提供了多个用于数据流的类,其中一个是BehaviorSubject。

Observables:Observables是随时间推移的多个值的惰性集合。 br />
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


#10 楼

BehaviorSubject
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');

Observables
开始,我们将研究创建常规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));