• 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

  • 相关阅读:
    [职场]最近聊到30岁以上的程序员,该何去何从了?你有啥想法?
    想跳槽涨薪,想进大厂,如何准备面试呢?
    [面试分享]想跳槽涨薪,想进大厂,如何准备面试呢?
    缓存穿透、缓存并发、缓存雪崩、缓存抖动、热点缓存、缓存双写一致性等问题...
    8天玩转并行开发——第八天 用VS性能向导解剖你的程序
    8天入门wpf—— 第一天 基础概念介绍
    8天玩转并行开发——第六天 异步编程模型
    8天入门wpf—— 第八天 最后的补充
    6天通吃树结构—— 第二天 平衡二叉树
    8天入门wpf—— 第七天 画刷
  • 原文地址:https://www.cnblogs.com/magicalconch/p/14457566.html
Copyright © 2020-2023  润新知