行为主体vs可观察对象?

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

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

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

599429 次浏览

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

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

主题类继承了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();

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

行为主体是一种主题类型,主题是一种特殊类型的可观察对象,因此你可以像任何其他可观察对象一样订阅消息。

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

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

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

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

可观测是一个泛型,BehaviorSubject在技术上是Observable的子类型,因为Be这辆车是一个具有特定品质的可观察对象。

行为主体为例:

// Behavior Subject
// a is an initial value. if there is a subscription// after this, it would get "a" value immediatelylet 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 cbSubject.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 csubject.next("d"); // Subscription got d

可以使用subject.asObservable()SubjectBehaviorSubject创建一个observable。

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

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

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

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

代码为每个观察者运行.如果是HTTP调用,则为每个观察者调用

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

行为主体(或主题)存储观察者详细信息,只运行一次代码并将结果提供给所有观察者。

例如:

JSBin:http://jsbin.com/qowulet/edit?js控制台

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

Output :

"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将Be这辆车转换为Observable时,它会继承在订阅时返回最后一个值的行为。

这是棘手的一点,因为通常库会将字段公开为可观察的(即Angular2中ActivatedRoute中的参数),但可能会在幕后使用主题或行为主题。他们使用的内容会影响订阅的行为。

看这里http://jsbin.com/ziquxapubo/edit?html、js、控制台

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,所以我只分享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是这里的局部变量。添加属性消息:字符串;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

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

可观测题目都是可观察的,这意味着观察者可以跟踪它们。它们都有一些独特的特征。有3种类型的主题,每种主题也有独特的特征。

您可以在堆栈闪电战上找到实际示例。(您需要检查控制台才能看到实际输出)

在此处输入图片描述

Observables

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

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

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

Subject

他们很热:代码被执行,值被广播,即使没有观察者。

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

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

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

ReplaySubject

他们很热:代码被执行,值被广播,即使没有观察者。

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

双向:观察者可以为observable赋值(原始/主)。加上

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

在主题和重放主题中,你不能将初始值设置为observable。所以这里是行为主体

BehaviorSubject

他们很热:代码被执行,值被广播,即使没有观察者。

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

双向:观察者可以为observable赋值(原始/主)。加上

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

可以设置初始值:你可以使用默认值初始化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});

行为主体 vs可观测:RxJS有观察者和可观察对象,Rxjs提供了多个用于数据流的类,其中一个是行为主题。

可观测数据:Observables是随时间变化的多个值的惰性集合。

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

 // RxJS v6+import { BehaviorSubject } from 'rxjs';
const subject = new BehaviorSubject(123);
//two new subscribers will get initial value => output: 123, 123subject.subscribe(console.log);subject.subscribe(console.log);
//two subscribers will get new value => output: 456, 456subject.next(456);
//new subscriber will get latest value (456) => output: 456subject.subscribe(console.log);
//all three subscribers will get new value => output: 789, 789, 789subject.next(789);
// output: 123, 123, 456, 456, 456, 789, 789, 789

可观测数据想象成一个管道,里面有流动的水,有时水流动,有时没有。在某些情况下,你可能真的需要一个管道,里面总是有水,你可以通过创建一个特殊的管道来做到这一点,它总是包含水,不管它有多小,让我们称这个特殊的管道行为主体,如果你碰巧是你社区的供水供应商,你可以在晚上安然入睡,知道你新安装的管道只是工作。

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

const firstNameChanges = new BehaviorSubject("<empty>");
// pass value changes.firstNameChanges.next("Jon");firstNameChanges.next("Arya");

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

firstNameChanges.value;

当您稍后组合Observables时,这会很方便,通过将流的类型视为Be这辆车,您可以确保流式传输至少一次火灾或信号

行为主体

行为主体建立在与我们的重放主体相同的功能之上,主题喜欢、热和重放以前的值。

行为主体增加了一个功能,因为你可以给行为主体一个初始值。让我们继续看一下这段代码

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

可观察是一个通用的,

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

只是一个函数,没有状态

为每个观察者运行代码

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

从技术上讲是Observable的一个子类型,因为Be这辆车是具有特定品质的可观察对象。

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

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

行为主体的独特功能是如下:

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

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

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

rxjs中的主题本质上是一个观察者和observable混搭在一起。观察者是我们放入值的东西,observable是我们可以观察值的东西。

  • 主题默认为热。Observable默认为冷。这意味着在有人订阅它之前它们不会发出任何值。我们创建主题的那一刻,我们可以从中发出一个值,即使还没有人订阅它,该值也会被发出。
  • 主题默认为多播。Observable默认为单播,这意味着对于我们拥有的每个不同的观察者,我们必须继承一个observable,如果该observable发出一个值,该值将为每个订阅者流过我们管道内的所有不同操作符一次。多播意味着所有其他操作符将为每个值运行一次,无论我们拥有的观察者数量如何。
  • GOTCHA=thE SUBJECT是多播的,但是如果你将管道语句链接到它,那将返回一个新的observable,它是冷的和单播的。

行为主体与主体相同,但也需要初始“种子”值。新订户立即获得最新值。如果有人订阅了行为主体,它将立即收到任何最新值。所以行为主体总是有一些价值可以给订户。

行为主题最有用的事情是当我们开始发出网络请求时。想象一下,我们将一些管道内容链接到行为主题,并在管道函数或管道操作符内部,我们最终发出网络请求并获取一些数据。你最终可能希望让其他东西订阅该observable,并立即获取已经获取的数据。使用行为主题,我们可以轻松实现这种行为。

我认为Observable是主题的包装器。而Observable只用于订阅数据更改。主题也可以用来通知订阅者数据更改(使用next()方法)。这是一个小的observable模式实现,可能会帮助你理解这个概念。TypeScript Playground

为了清楚起见,你也可以将主题更改为可观察对象,如下所示:

 page = new BehaviorSubject<String|null>(null);actualPage:Observable<string> = new Observable()
this.page.next("hardware")this.actualPage = this.page as Observable<any>;