Answers:
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
和两者创建可观察对象。BehaviorSubject
subject.asObservable()
唯一的区别是您不能将值发送给可观察的using next()
方法。
在Angular服务中,我将使用BehaviorSubject
数据服务,因为Angular 服务通常在组件和行为主体确保使用该服务的组件接收到最后更新的数据之前进行初始化,即使自组件订阅该数据以来没有新的更新也是如此。
一个非常非常重要的区别。由于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 ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
KnockoutJS's ko.observable()
,Rx.BehaviorSubject
与Rx.Observable
可观察和主题都是可观察的,观察者可以跟踪它们。但是它们都有一些独特的特征。此外,共有3种主题,每个主题又具有独特的特征。让我们尝试了解它们中的每一个。
您可以在stackblitz上找到实际示例。 (您需要检查控制台以查看实际输出)
Observables
它们很冷:只有至少一个观察者时,代码才能执行。
创建数据副本: Observable为每个观察者创建数据副本。
单向:观察者无法将值分配给可观察(原始/主)。
Subject
它们很热:即使没有观察者,代码也会被执行,价值也会得到传播。
共享数据:在所有观察者之间共享相同的数据。
双向:观察者可以将值分配给可观察(原始/主)。
如果正在使用主题,则您会错过创建观察者之前广播的所有值。所以这是重播主题
ReplaySubject
它们很热:即使没有观察者,代码也会被执行,价值也会得到传播。
共享数据:在所有观察者之间共享相同的数据。
双向:观察者可以将值分配给可观察(原始/主)。加
重播消息流:无论何时订阅重播主题,您都将收到所有广播的消息。
在主题和重播主题中,您无法将初始值设置为可观察。所以在这里谈到的行为主体
BehaviorSubject
它们很热:即使没有观察者,代码也会被执行,价值也会得到传播。
共享数据:在所有观察者之间共享相同的数据。
双向:观察者可以将值分配给可观察(原始/主)。加
重播消息流:无论何时订阅重播主题,您都将收到所有广播的消息。
您可以设置初始值:您可以使用默认值初始化可观察对象。
ReplaySubject
具有历史记录,可以广播/发出一系列(旧)值。仅当buffer设置为1时,其行为才类似于a BehaviorSubject
。
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的更多信息
在示例中我没有看到的一件事是,当您通过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
。
让我们通过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
。向应用程序提供此服务。
导入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();
}
}
而已。
现在,任何输入值中#message
的home.component.html
应打印到{{message}}
内app.component.html
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
});
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
可以将Observables看作是其中有流动水的管道,有时水会流动,有时却没有。在某些情况下,您实际上可能需要一个总是有水的管道,您可以通过创建一个特殊的管道来做到这一点,无论管道有多小,该管道总是包含水,如果您碰巧是这样,可以称之为特殊管道BehaviorSubject。社区的水供应商,知道新安装的管道可以正常工作,您就可以在晚上安然入睡。
用技术术语来说:您可能会遇到用例,在该用例中,一个Observable应该始终具有值,也许您想随着时间的推移捕获输入文本的值,然后可以创建BehaviorSubject实例来确保这种行为,让我们说:
const firstNameChanges = new BehaviorSubject("<empty>");
// pass value changes.
firstNameChanges.next("Jon");
firstNameChanges.next("Arya");
然后,您可以使用“值”来采样随时间变化的样本。
firstNameChanges.value;
以后结合Observable时,这很方便,通过将流的类型视为BehaviorSubject,可以确保流至少触发或发信号至少一次。