我正在研究 Angular RxJs 模式,但我不明白 BehaviorSubject
和 Observable
之间的区别。
据我了解,BehaviorSubject
是一个可以随时间变化的值(可以订阅并且订阅者可以接收更新的结果)。这似乎与 Observable
的目的完全相同。
您什么时候使用 Observable
和 BehaviorSubject
?使用 BehaviorSubject
而不是 Observable
是否有好处,反之亦然?
BehaviorSubject 是一种主题,主题是一种特殊类型的可观察对象,因此您可以像订阅任何其他可观察对象一样订阅消息。 BehaviorSubject 的独特之处在于:
它需要一个初始值,因为它必须始终在订阅时返回一个值,即使它没有收到 next()
订阅后,它返回主题的最后一个值。常规的 observable 仅在收到 onnext 时触发
在任何时候,您都可以使用 getValue() 方法在不可观察的代码中检索主题的最后一个值。
与可观察对象相比,对象的独特特征是:
除了作为可观察对象之外,它还是一个观察者,因此除了订阅它之外,您还可以向主题发送值。
此外,您可以使用 BehaviorSubject
上的 asObservable()
方法从行为主题中获取 observable。
Observable 是 Generic,而 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 won't get
// anything at this point
});
subject.next("c"); // Subscription got c
subject.next("d"); // Subscription got d
可以使用 subject.asObservable()
从 Subject
和 BehaviorSubject
创建 observable。
唯一的区别是您不能使用 next()
方法将值发送到可观察对象。
在 Angular 服务中,我会将 BehaviorSubject
用于数据服务,因为 Angular 服务通常在组件之前初始化,并且行为主体确保使用该服务的组件接收到最后更新的数据,即使自组件订阅后没有新的更新也是如此数据。
Observable:每个观察者的不同结果
一个非常非常重要的区别。由于 Observable 只是一个函数,它没有任何状态,所以对于每一个新的 Observer,它都会一次又一次地执行 observable 创建代码。这导致:
代码为每个观察者运行。如果它是一个 HTTP 调用,它会被每个观察者调用
这会导致重大错误和效率低下
BehaviorSubject (或 Subject )存储观察者详细信息,只运行一次代码并将结果提供给所有观察者。
前任:
JSBin:http://jsbin.com/qowulet/edit?js,console
// --- Observable --- 让 randomNumGenerator1 = Rx.Observable.create(observer => { observer.next(Math.random()); });让observer1 = randomNumGenerator1 .subscribe(num => console.log('observer 1: '+ num));让observer2 = randomNumGenerator1 .subscribe(num => console.log('observer 2: '+ num)); // ------ BehaviorSubject/ Subject let randomNumGenerator2 = new Rx.BehaviorSubject(0); randomNumGenerator2.next(Math.random()); letobserver1Subject = randomNumGenerator2 .subscribe(num=> console.log('observer subject 1: '+ num)); letobserver2Subject = randomNumGenerator2 .subscribe(num=> console.log('observer subject 2: '+ num));
输出 :
"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's ko.observable()
的任何人都会立即看到与 Rx.BehaviorSubject
的更多相似之处
Observable 和 Subject 都是 observable 的,这意味着观察者可以跟踪它们。不过,它们都有一些独特的特征。此外,还有 3 种类型的主题,每一种都具有独特的特点。
您可以在 stackblitz 上找到实际示例。 (您需要检查控制台才能看到实际输出)
https://i.stack.imgur.com/oLsZ4.png
Observables
他们很冷:当他们至少有一个观察者时,代码就会被执行。
创建数据副本:Observable 为每个观察者创建数据副本。
单向:Observer 不能为 observable(origin/master) 赋值。
Subject
它们很热:即使没有观察者,代码也会被执行并且值会被广播。
共享数据:所有观察者之间共享相同的数据。
双向:Observer 可以为 observable(origin/master) 赋值。
如果正在使用主题,那么您会错过在创建观察者之前广播的所有值。所以来了重播主题
ReplaySubject
它们很热:即使没有观察者,代码也会被执行并且值会被广播。
共享数据:所有观察者之间共享相同的数据。
双向:Observer 可以为 observable(origin/master) 赋值。加
重播消息流:无论您何时订阅重播主题,您都会收到所有广播的消息。
在 Subject 和 ReplaySubject 中,不能将初始值设置为 observable。所以这里出现了 BehavioralSubject...
BehaviorSubject
它们很热:即使没有观察者,代码也会被执行并且值会被广播。
共享数据:所有观察者之间共享相同的数据。
双向:Observer 可以为 observable(origin/master) 赋值。加
重播消息流:无论您何时订阅重播主题,您都会收到所有广播的消息。
您可以设置初始值:您可以使用默认值初始化 observable。
ReplaySubject
具有历史记录并且可以广播/发出一系列(旧)值。只有当缓冲区设置为 1 时,它的行为类似于 BehaviorSubject
。
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-Extensions/RxJS/blob/master/doc/gettingstarted/subjects.md
我在示例中没有看到的一件事是,当您通过 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 只允许您订阅,而 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.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
处生成服务。提供this service to the app。
将 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}}
将 Observables 想象成一个管道,里面有流动的水,有时水会流动,有时不会。在某些情况下,您实际上可能需要一个始终有水的管道,您可以通过创建一个始终包含水的特殊管道来做到这一点,无论它有多小,如果您碰巧是,我们将此特殊管道称为 BehaviorSubject作为您社区的供水供应商,您知道新安装的管道正常工作,您可以在晚上安然入睡。
用技术术语来说:您可能会遇到 Observable 应该始终具有价值的用例,也许您想随着时间的推移捕获输入文本的值,然后您可以创建 BehaviorSubject 的实例来确保这种行为,让我们说:
const firstNameChanges = new BehaviorSubject("<empty>");
// pass value changes.
firstNameChanges.next("Jon");
firstNameChanges.next("Arya");
然后,您可以使用“值”对随时间的变化进行采样。
firstNameChanges.value;
当您稍后组合 Observables 时,这会很方便,通过查看流的类型为 BehaviorSubject,您可以确保流至少触发或至少发出一次信号。
app.component.ts
behaviourService.setName("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:需要初始值并将其当前值发送给新订阅者的主题。
// 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
Observable 是一个泛型,
Observables 是多个值随时间推移的惰性集合。
只是一个函数,没有状态
为每个观察者运行代码
BehaviorSubject:需要初始值并将其当前值发送给新订阅者的主题。
从技术上讲,它是 Observable 的子类型,因为 BehaviorSubject 是具有特定品质的可观察对象。
有状态。将数据存储在内存中
相同的代码对所有观察者只运行一次
BehaviorSubject 的独特之处如下:
它需要一个初始值,因为它必须始终在订阅时返回一个值,即使它没有收到 next()
订阅后,它返回主题的最后一个值。常规的 observable 仅在收到 onnext 时触发
在任何时候,您都可以使用 getValue() 方法在不可观察的代码中检索主题的最后一个值。
rxjs 中的主题本质上是一个观察者和可观察者的混合体。 Observer 是我们投入值的东西,observable 是我们可以观察值的东西。
主题默认为热门。 Observables 默认是冷的。这意味着他们不会发出任何值,直到有人订阅它。在我们创建一个主题的那一刻,我们可以从中发出一个值,即使没有人订阅它,该值也会被发出。
主题默认为多播。默认情况下,Observable 是单播的,这意味着对于我们拥有的每个不同的观察者,我们必须订阅一个 observable,如果该 observable 发出一个值,该值将为每个订阅者流过管道内的所有不同运算符一次.多播意味着所有其他运算符将为每个值运行一次,无论我们拥有多少观察者。
GOTCHA= THE SUBJECT 是多播的,但是如果您将管道语句链接到它,那将返回一个新的冷单播的 observable。
行为主体与主体相同,但也采用初始“种子”值。新订户立即获得最新价值。如果有人订阅了 Behavior 主题,它将立即收到最新的值。因此,行为主题总是有一些价值可以提供给订阅者。
行为主体最有用的事情是当我们开始发出网络请求时。想象一下,我们将一些管道内容链接到行为主体,并在管道函数或管道运算符内部,我们最终发出网络请求并获取一些数据。您最终可能希望让其他东西订阅该 observable 并立即获取已经获取的数据。使用行为主体,我们可以轻松实现这种行为。
行为主体
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');
可观察的
首先,我们将查看创建常规 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 仅用于订阅数据更改。 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>;
BehaviourSubject
并且值是从发出BehaviourSubject
值的公共Observable
访问的,因此不允许在服务外部的BS
上调用next
?