天天看点

Observable与Subject

#介绍

首先,对象关系上,Subject是Observable的子类,相比拥有了多播的效果,使得多个订阅者订阅的对象数据是共享的,而在它下面又细分了AnonymousSubject, AsyncSubject, BehaviorSubject三个子类,分别用于处理不同场景的业务需求。

#Observable和Subject的差异

  • Observable的每个订阅者之间,读取的发布数据是相对各自独立的。
  • Subject的订阅者之间,是共享一个发布数据的。

#Subject的子类详情

1. BehaviorSubject

Subject的主要子类是BehaviorSubject,使用BehaviorSubject具有存储当前“值”的特性,这意味着我们始终可以直接从BehaviorSubject获取最后发出的值。

获取值一共有两种方法

  1. 通过访问.value属性来获取值
  2. 通过订阅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
           
  1. 我们首先创建一个主题并使用Subscriber A订阅它。然后,Subject将发出它的值,订阅者A将记录随机数。
  2. 主题发出它的下一个值。订户A将再次记录此信息
  3. 订阅者B从订阅主题开始。由于主题是BehaviorSubject,新订阅者将自动接收最后存储的值并记录此信息。
  4. 主题再次发出新值。现在,两个订阅者都将收到值并记录它们。
  5. 最后,我们只需访问该.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才能访问):

原文:https://medium.com/@luukgruijs/understanding-rxjs-behaviorsubject-replaysubject-and-asyncsubject-8cc061f1cfc0

继续阅读