#介绍
首先,对象关系上,Subject是Observable的子类,相比拥有了多播的效果,使得多个订阅者订阅的对象数据是共享的,而在它下面又细分了AnonymousSubject, AsyncSubject, BehaviorSubject三个子类,分别用于处理不同场景的业务需求。
#Observable和Subject的差异
- Observable的每个订阅者之间,读取的发布数据是相对各自独立的。
- Subject的订阅者之间,是共享一个发布数据的。
#Subject的子类详情
1. BehaviorSubject
Subject的主要子类是BehaviorSubject,使用BehaviorSubject具有存储当前“值”的特性,这意味着我们始终可以直接从BehaviorSubject获取最后发出的值。
获取值一共有两种方法
- 通过访问.value属性来获取值
- 通过订阅BehaviorSubject来获取值,BehaviorSubject将直接向订阅者发出当前值。即使订阅者订阅的时间远远晚于存储的值。
import * as Rx from "rxjs";
const subject = new Rx.BehaviorSubject();
// 订阅对象1
subject.subscribe((data) => {
console.log('Subscriber A:', data);
});
//发布新的值
subject.next(Math.random());
subject.next(Math.random());
// 订阅对象2
subject.subscribe((data) => {
console.log('Subscriber B:', data);
});
// 再次发布新的值
subject.next(Math.random());
console.log(subject.value)
// 输出日志
// Subscriber A: 0.24957144215097515
// Subscriber A: 0.8751123892486292
// Subscriber B: 0.8751123892486292
// Subscriber A: 0.1901322109907977
// Subscriber B: 0.1901322109907977
// 0.1901322109907977
- 我们首先创建一个主题并使用Subscriber A订阅它。然后,Subject将发出它的值,订阅者A将记录随机数。
- 主题发出它的下一个值。订户A将再次记录此信息
- 订阅者B从订阅主题开始。由于主题是BehaviorSubject,新订阅者将自动接收最后存储的值并记录此信息。
- 主题再次发出新值。现在,两个订阅者都将收到值并记录它们。
- 最后,我们只需访问该.value属性即可记录当前的Subjects值 ,因为它是同步的,可以不用通过订阅读取值。
最后,我发现现在已经需要在创建对象的时候赋予初始值,如下示例:
import * as Rx from "rxjs";
// 参数:初始值
const subject = new Rx.BehaviorSubject(Math.random());
2.ReplaySubject
ReplaySubject可以与BehaviorSubject相比,它可以向新订阅者发送“旧”的历史值。借此特性,我们可以用它可以记录可观察对象执行的一部分,因此存储多个旧值并将它们“重放”给新的订阅者。
创建ReplaySubject时,你可以指定要存储的值的数量以及要存储它们的时间长度。
import * as Rx from "rxjs";
// 存储2个值,这些值在新订阅之前的最后一秒执行
const subject = new Rx.ReplaySubject(2);
// 开始使用订阅者A订阅主题
subject.subscribe((data) => {
console.log('Subscriber A:', data);
});
// 发布三个新值
subject.next(Math.random())
subject.next(Math.random())
subject.next(Math.random())
// 订阅B
subject.subscribe((data) => {
console.log('Subscriber B:', data);
});
// 发布新值
subject.next(Math.random());
// 输出日志
// Subscriber A: 0.3541746356538569
// Subscriber A: 0.12137498878080955
// Subscriber A: 0.531935186034298
// Subscriber B: 0.12137498878080955
// Subscriber B: 0.531935186034298
// Subscriber A: 0.6664809293975393
// Subscriber B: 0.6664809293975393
根据输出来看,订阅者A记录了三个发布值。
由于我们告诉ReplaySubject存储2个值,它将直接向订阅者B发送最后两个值,订阅者B于是只输出了最后发布的两个值。
除此之外,我们还可以指定存储值的时间,如下所示。
import * as Rx from "rxjs";
//创建ReplaySubject并指定只存储最后2个值,但不超过100毫秒
const subject = new Rx.ReplaySubject(2, 100);
// 创建订阅A
subject.subscribe((data) => {
console.log('Subscriber A:', data);
});
// 每隔200毫秒开始发出主题值。订阅者A将选择此项并记录主题发出的每个值
setInterval(() => subject.next(Math.random()), 200);
// 创建订阅B
setTimeout(() => {
subject.subscribe((data) => {
console.log('Subscriber B:', data);
});
}, 1000)
// 输出日志
// Subscriber A: 0.44524184251927656
// Subscriber A: 0.5802631630066313
// Subscriber A: 0.9792165506699135
// Subscriber A: 0.3239616040117268
// Subscriber A: 0.6845077617520203
// Subscriber B: 0.6845077617520203
// Subscriber A: 0.41269171141525707
// Subscriber B: 0.41269171141525707
// Subscriber A: 0.8211466186035139
// Subscriber B: 0.8211466186035139
如上所示,我们开始订阅者B后,因为我们在1000毫秒后执行此操作,这意味着在开始订阅之前,主题已经发出了5个值。
这意味着在1000毫秒之后,当订阅者B开始订阅时,它将仅接收1个值,因为主体每200ms发出一次值。
3. AsyncSubject
虽然BehaviorSubject和ReplaySubject都存储值,但AsyncSubject的工作方式略有不同。AsyncSubject是一个主题变体,只有Observable执行的最后一个值才会发送给它的订阅者,并且只有在执行完成时才会发送.
import * as Rx from "rxjs";
// 创建了AsyncSubject
const subject = new Rx.AsyncSubject();
// 创建订阅A
subject.subscribe((data) => {
console.log('Subscriber A:', data);
});
subject.next(Math.random())
subject.next(Math.random())
subject.next(Math.random())
// 创建订阅B
subject.subscribe((data) => {
console.log('Subscriber B:', data);
});
subject.next(Math.random());
subject.complete();
// 输出日志
// Subscriber A: 0.4447275989704571
// Subscriber B: 0.4447275989704571
如上我们能够观察到,A并没有输出发布的前三个值,是因为只有当订阅对象complete完成后,才会将值发送给订阅者。
#结论
BehaviorSubject,ReplaySubject和AsyncSubject不仅可以像使用普通Subject一样用于多播,而且它们在不同场景中各自有具有非常方便的特性,我们可以结合业务场景和需求具体使用。
PS: 如上教程多为译文,原文地址如下(可能需要VPN才能访问):