BehaviorSubject 与 Observable?

BehaviorSubject vs Observable?

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

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

您什么时候会使用 ObservableBehaviorSubject?在 Observable 上使用 BehaviorSubject 有什么好处,反之亦然?

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

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

Subject class 继承了 Observable 和 Observer,在某种意义上它既是观察者又是可观察者。可以用一个subject订阅所有的观察者,然后subject订阅一个后端数据源

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

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

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

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

  • 除了是可观察对象之外,它还是观察者,因此除了订阅对象之外,您还可以向对象发送值。

此外,您可以在 BehaviorSubject.

上使用 asObservable() 方法从行为主题中获取可观察对象

Observable 是一个泛型,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 won't get 
                                          // anything at this point
});

subject.next("c"); // Subscription got c
subject.next("d"); // Subscription got d

可以使用 subject.asObservable().

SubjectBehaviorSubject 创建可观察对象

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

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

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

一个非常非常重要的区别。由于 Observable 只是一个函数,它没有任何状态,所以对于每一个新的 Observer,它都会一次又一次地执行 observable 创建代码。这导致:

The code is run for each observer . If its a HTTP call, it gets called for each observer

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

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

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

observable allows you to subscribe only whereas a 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组件。将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 的 class.

运行 这个命令 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:

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

就是这样。

现在,在 home.component.html#message 中输入的任何值都将打印到 app.component.html

中的 {{message}}

ObservableSubject 都是可观察的,这意味着观察者可以跟踪它们。但是,它们都具有一些独特的特征。此外,还有 3 种类型的 Subject,每种类型也都有独特的特征。

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

Observables

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

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

单向:Observer不能给observable赋值(origin/master).

Subject

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

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

双向:观察者可以给observable赋值(origin/master).

如果使用 using subject,那么您会错过在创建观察者之前广播的所有值。所以来了重播主题

ReplaySubject

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

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

双向:Observer可以给observable赋值(origin/master)。加上

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

在Subject和ReplaySubject中,不能将初始值设置为observable。所以来了 BehavioralSubject...

BehaviorSubject

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

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

双向:Observer可以给observable赋值(origin/master)。加上

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

你可以设置初始值:你可以用默认值初始化observable。

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

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

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

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

 // 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

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

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


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

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

然后您可以使用 "value" 来采样随时间的变化。


firstNameChanges.value;

这在您稍后组合 Observables 时会派上用场,通过查看流的类型作为 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 的方法是实例化 class。其他运算符可以简化这一点,但我们需要将实例化步骤与我们不同的 Observable 类型进行比较

import { Observable } from 'rxjs';

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

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

可观察 是通用的,

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

只是一个函数,没有状态

每个观察者的代码 运行

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

在技术上是 Observable 的子类型,因为 BehaviorSubject 是具有特定特性的可观察对象。

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

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

BehaviorSubject 的独特功能是以下:

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

订阅后,它 return 是主题的最后一个值。常规可观察对象仅在收到 onnext

时触发

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

rxjs 中的 Subject 本质上是一个 observer 和 observable 混合在一起的。 Observer 是我们投入值的东西,observable 是我们可以观察值的东西。

  • 主题默认为热门。 Observable 默认是冷的。这意味着他们不会发出任何值,直到有人订阅它。在我们创建主题的那一刻,我们可以从中发出一个值,即使还没有人订阅它,该值也会被发出。
  • 主题默认为多播。默认情况下,Observable 是单播的,这意味着对于我们拥有的每个不同的观察者,我们必须订阅一个 observable,如果该 observable 发出一个值,该值将为每个订阅者流过我们管道内的所有不同运算符一次.多播意味着所有其他运算符将为每个值 运行 一次,无论我们有多少观察者。
  • GOTCHA= THE SUBJECT 是多播的,但如果您将管道语句链接到它,那将 return 一个新的冷单播可观察对象。

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

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

我认为 Observable 是 Subject 的包装器。而 Observable 仅用于订阅数据变化。 Subject 也可以用来通知订阅者数据的变化(使用 next() 方法)。这是一个小的可观察模式实现,可以帮助您理解这个概念。 TypeScript Playground

需要说明的是,您也可以像这样将主题更改为可观察对象:

 page = new BehaviorSubject<String|null>(null);
 actualPage:Observable<string> = new Observable()

this.page.next("hardware")
this.actualPage = this.page as Observable<any>;