BehaviorSubject与Observable?


689

我正在研究Angular RxJs模式,但不了解a BehaviorSubject和an 之间的区别Observable

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

什么时候使用Observablevs BehaviorSubject?在那里使用效益BehaviorSubject上的Observable,反之亦然?

Answers:


967

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

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

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

  • 它不仅是可观察者,而且是观察者,因此您除了订阅它之外,还可以将值发送给主题。

此外,您可以使用上的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

可以使用Subject和两者创建可观察对象。BehaviorSubjectsubject.asObservable()

唯一的区别是您不能将值发送给可观察的using next()方法。

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


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

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

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

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

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

181

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

一个非常非常重要的区别。由于Observable只是一个函数,它没有任何状态,因此对于每个新的Observer,它都会一次又一次地执行可观察的创建代码。结果是:

该代码针对每个观察者运行。如果是HTTP调用,则会为每个观察者调用

这会导致重大错误和效率低下

BehaviorSubject(或Subject)存储观察者详细信息,仅运行一次代码并将结果提供给所有观察者。

例如:

JSBin: http

// --- 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.BehaviorSubjectRx.Observable
Simon_Weaver

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

61

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

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

在此处输入图片说明

Observables

它们很冷:只有至少一个观察者时,代码才能执行。

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

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

Subject

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

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

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

如果正在使用主题,则您会错过创建观察者之前广播的所有值。所以这是重播主题

ReplaySubject

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

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

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

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

在主题和重播主题中,您无法将初始值设置为可观察。所以在这里谈到的行为主体

BehaviorSubject

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

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

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

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

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


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

28

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

Observer和Observable接口提供了基于推送的通知的通用机制,也称为观察者设计模式。Observable对象表示发送通知的对象(提供者);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

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

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

20

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

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组件。将以下内容替换为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();
  }
}

而已。

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


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

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

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

1
@ruffin如果它违反了社区的同意,那么它肯定不应该在那里!
xameeramir '18 -10-6

4

app.component.ts

behaviourService.setName("behaviour");

行为服务

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具有观察者和可观察对象,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

1

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

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


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

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

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


firstNameChanges.value;

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.