通过 Marble Test 理解 RxJS

本文详细介绍了如何通过Marble Test和Marble Diagram理解RxJS中的Observable行为。内容涵盖Marble Test语法,包括cold和hot的区别,以及如何使用Marble字符串模拟Observable的状态。此外,还通过具体示例展示了takeUntil、take和switchMap等操作符的工作原理。
摘要由CSDN通过智能技术生成

上篇文章介绍了一些 RxJS 的相关概念,本文通过学习 Marble Test 进一步的理解 RxJS。

Marble Diagram 是理解 RxJS 的重要辅助工具,在 RxJS 的文档中有很多以时间为轴的图,那就是 Marble Diagram。而 Marble Test 就是测试某个 Observable 是否满足某个 Marble Diagram 的方法,能帮助我们更好地理解“时间”在 RxJS 中到底是起到了什么作用,也能够让我们更好地理解 Observable 的转换(如通过各种 operators)到底发生了什么。

本文涉及的代码均在 RxJS v6 版本,此版本官方提供了rxjs/testing,在此版本之前可能需要外部的测试库辅助,但基本概念是类似的。

Marble Diagram

Marble Diagram 是描述 Observable 状态重要的可视化工具,它提供了一种模式用来描述我们的 Observable 发生了什么:

  • 处理中:准备数据的阶段,这些时刻不会和 Observer 有交流
  • 发出数据:通知 Observer 的 next
  • 完成:通知 Observer 的 complete
  • 出错:通知 Observer 的 error

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5X2VCWFZ-1611228499572)(/img/posts/rxjs/rxjs-marble-diagram.png)]
Marble Diagram

上图中可以看到,横轴是时间,且流向是从左到右。圆形表示发出的数据,也就是 marble,所以才叫做 Marble Diagram。可以很容易地想象 Observable 产生 marble 在时间轴上的分布应该是有规律可循的,比如:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BWGxC8Mt-1611228499574)(/img/posts/rxjs/timer.png)]
timer(3000, 1000)的 Marble Diagram

timer(3000, 1000) 的含义是指 3s 后开始发出第一个数据,然后每隔 1s 发出下一个数据,所以 0 之前的时间长度应该是任意两个数据之间时间长度的 3 倍。

那我们如何在测试中提现这种类似的时间关系呢?我们可以通过一种特定的语法来表示 Marble Diagram 中状态,这就是 Marble Test 的核心。

Marble Test 语法

  • - 连接符,表示时间片,在测试用例中可以将 1 个时间片等同为 1ms,所以 ---- 时间长度为 4ms(如果出现的只有连接符,如 ----- 则表示一个不会发出任何值,也不会结束的 Observable,和 NEVER 一样)
  • [a-z0-9] 小写英文字母或数字,表示发出数据,但并不表示发出数据的值一定是他们本身,Marble Test 支持提供额外的数据映射,而它们也会占用一个时间片,所以在没有提供额外的数据映射时 --a-0-b- 表示 3ms 时发出 ‘a’,5ms 时发出 ‘0’(默认是字符串类型)
  • [0-9]+[ms|s|m] 即数字加上时间单位,比如 9m 表示 9 分钟,等同于 9*60s,因为单位时间为 1ms,如果只使用上面连接符的方式在表示一些时间比较长的 Observable 比较繁琐。3ms a 1ms---a- 是等价的
  • ' ' 空白字符,上面的例子中出现了空格,它是不占用时间片的,可以用于类似上面这种必须分隔开的场景(3msa1ms 的含义完全不同),也可以用于代码的可读性
  • | 表示图中的 complete,即告知 Observer 的 complete 方法,-a--| 表示 2ms 时发出 ‘a’,5ms 时结束
  • # 表示图中的 error,即告知 Observer 的 error 方法,-a-# 表示 2ms 时发出 ‘a’,4ms 时出现错误
  • () 上面的图中,每个数据的发出、结束、出错看起来都是有时间间隔的,但现实肯定不总是这样,可能在同一个时间片中有多个数据的发出,但是不能写成类似 -ab- 的形式,因为 ‘a’ 和 ‘b’ 占用了不同的时间片,想要表示在同一个时间片时就需要使用括号了,所以 -(ab)- 表示 2ms 时同时发出了 ‘a’ 和 ‘b’
  • ^ 表示 Observer 订阅的时刻,只对 hot Observable 生效
  • ! 表示 Observer 退订的时刻,注意和 | 的区别,前者是 Observer 不再对数据感兴趣了,后者是 Observable 已经发出了所有了数据

由上面的语法组成的字符串,比如 -(ab)- 叫做 Marble 字符串,可以看到利用这个字符串可以模拟出时间的流逝、数据的发出、完成、错误、订阅开始、订阅结束的这些状态。

关于 cold 和 hot

cold Observable 和 hot Observable 的差异性是学习 RxJS 绕不开的概念,而且经常和 Observable 的 lazy 特性搞混。无论是 cold 还是 hot,都具有 lazy 的特性,即都是在 Observer 进行了 subscribe 操作时才对 Observer 推送数据。

某种意义上来说它们区别在于数据的来源,一般来说 cold Observable 自己维护着数据,无论何时一个 Observer 的订阅的到来,都从头将数据推送到 Observer;而 hot Observable 的数据来源于外部,比如最经常用到的 fromEvent 将事件流转换为 Observable,即使没有 Observer 进行订阅,事件是时刻在发生着的,当 Observer 订阅时只能收到下一次的事件,且对多个 Observer 数据是共享的。

由于超出本文的范围,本文不会对 cold 和 hot 的概念做深入的解释,感兴趣的大家可以先看下官方解释

如何使用 Marble 字符串

上面提到了 Marble 字符串的语法,那如何使用它们创建出对应的 Observable 呢?在使用 Marble Test 时,首先需要引入 rxjs/testingTestScheduler 类,然后生成一个 TestScheduler 类实例 testScheduler,所有的操作都在实例的 run 方法中完成:

import {
    expect } from "chai";
import {
    TestScheduler } from "rxjs/testing";

describe("test hot observables", () => {
   
  let testScheduler: TestSc
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值