当前位置: 首页 > news >正文

[译] 调试 RxJS 第1部分: 工具篇

原文链接: blog.angularindepth.com/debugging-r…
本文为 RxJS 中文社区 翻译文章,如需转载,请注明出处,谢谢合作!
如果你也想和我们一起,翻译更多优质的 RxJS 文章以奉献给大家,请点击【这里】

我是一位 RxJS 信徒,在我所有活跃的项目中都在使用它。用了它之后,我发现很多乏味的事现在都变得很简单。然而,有一件事却没有任何好转,那就是调试。

由于 RxJS 的可组合性与有时是异步的本质使得调试变成了一种挑战:没有太多的状态可以观察,而且调用堆栈基本也没什么帮助。我之前的做法是在整个代码库中穿插大量的 do 操作符和日志来检查流经组合 observables 的值。由于以下几点原因,我对这种方法并不满意:

  • 我总是在不断地添加日志,调试的同时还要更改代码
  • 调试完成后,对于日志,我要么一条条的进行手动删除,要么选择忍受
  • 匆忙之中将 do 操作符随意放置在一个组合 observable 中间时,应该避免有条件的日志输出的太恐怖
  • 即使是专门的日志操作符,其体验也不理想

最近,我花费了一些时间开发了一个 RxJS 的调试工具。它有如下几个功能,而且我觉得是这个工具必须要具备的:

  • 它应该尽可能地不唐突
  • 它应该不需要靠不断修改代码来进行调试
  • 特别是,它应该不需要解决问题后靠手动删除或注释掉调试代码
  • 它应该支持可以轻松启用和禁用的日志
  • 它应该支持捕获可以随时间进行比较的快照
  • 它应该提供一些与浏览器控制台的集成,用于打开/关闭调试功能和检查状态,等等

还有一些功能,如果能有就更好了:

  • 它应该支持暂停 observables
  • 它应该支持修改 observables 或 observables 发出的值
  • 它应该支持除控制台之外的日志机制
  • 它应该是可扩展的
  • 它应该采取一些方法来捕获可视化订阅依赖所需的数据

综合考虑这些功能后,我开发了 rxjs-spy

核心概念

rxjs-spy 引入了 tag 操作符,它将一个字符串标签和一个 observable 关联起来。这个操作符并没有以任何方式来改变 observable 的行为和值。

tag 操作符可以单独使用: import "rxjs-spy/add/operator/tag" 。这样的话,rxjs-spy 的其他方法会在生成版本中被忽略,所以唯一的开销就是字符串的使用 (导入)。

大多数工具方法都接受匹配器 ( matchers ),以确定它们即将应用哪些标记过的 observables 。匹配器可以是简单的字符串、正则表达式或传递标签本身的函数谓词 ( predicates )。

当通过调用工具的 spy 方法配置后,它会在 Observable.prototype.subscribe 上打补丁,这样它就能够侦察到所有的订阅、通知和取消订阅。当然,只有被订阅的 observables 才能通过 spy 进行侦察。

rxjs-spy 公开了一个模块 API 用于在代码中调用,还公开了一个控制台 API 供用户在浏览器的控制台中进行交互。大多数时候,我都是在应用的启动代码中早早地调用模块 API 的 spy 方法,然后使用控制台 API 来执行剩下的调试工作。

控制台 API 功能

调试时,我通常使用浏览器的控制台来检查和操纵标记过的 observables 。控制台 API 还是通过示例来解释比较容易,下面的代码示例展示了如何与 observables 配合使用:

import { Observable } from "rxjs/Observable";
import { spy } from "rxjs-spy";

import "rxjs/add/observable/interval";
import "rxjs/add/operator/map";
import "rxjs/add/operator/mapTo";
import "rxjs-spy/add/operator/tag";

spy();

const interval = new Observable
  .interval(2000)
  .tag("interval");

const people = interval
  .map((value) => {
    const names = ["alice", "bob"];
    return names[value % names.length];
  })
  .tag("people")
  .subscribe();复制代码

rxjs-spy 的控制台 API 是通过全局变量 rxSpy 公开的。

调用 rxSpy.show() 会显示所有标记过的 observables 列表,并表明它们的状态 (未完成、已完成或报错)、订阅者的数量以及最新发出的值 (如果有值发出的话)。控制台输出是像这样的:

要显示某个特定的标记 observable,需要将标签名或正则表达式传给 show

通过调用 rxSpy.log 可以启用某个标记 observable 的日志:

调用 log 时不带任何参数会启用所有标记 observables 的日志。

模块 API 的大部分方法会返回一个拆解函数,它用来解除方法的调用。在控制台中管理这些太麻烦了,所以还有另外一种选择。

调用 rxSpy.undo() 会显示所有调用过的方法的列表:

使用方法调用相关联的数字来调用 rxSpy.undo 会直接调用调用方法的拆解函数。例如,调用 rxSpy.undo(3) 会看到 interval observable 的日志停止输出:

有时候,当调试的同时修改 observable 或它的值是很有用的。控制台 API 包含 let 方法,它的作用同 RxJS 中的 let 操作符十分相似。它的实现方式是这样的:调用 let 方法会影响到标记 observable 的当前订阅者和将来的订阅者。例如,下图中的调用会看到 people observable 发出 mallory,而不是 alicebob

log 方法一样,let 方法的调用也可以取消:

对我来说,调试时能够暂停 observable 的功能几乎是不可或缺的。调用 rxSpy.pause 会暂停标记 observable 并返回一个用于控制和检查 observable 通知的 deck 对象:

调用 deck 的 log 方法会显示 observable 是否暂停和暂停期间的所有通知 (通知是使用 materialize 操作符获取的 RxJS 的 Notification 实例)。

调用 deck 的 setp 方法会发出一条通知:

调用 resume 方法会发出所有暂停期间的通知并恢复 observable:

调用 pause 会看到 observable 再次回到暂停状态:

很容易会忘记将返回的 deck 赋值给了哪个变量,所以控制台 API 还提供了 deck 方法,它的行为类似于 undo 方法。调用它会显示所有 pause 调用的列表:

使用 pause 调用相关联的数字来调用 deck 方法并会返回相关联的 deck 对象:

就像 loglet 调用一样,pause 调用也可以取消,并且取消 pause 调用会恢复标记的 observable:

希望上面的示例会让你对 rxjs-spy 以及它的控制台 API 有一个大致的了解。「 调试 RxJS 」系统的后续部分会专注于 rxjs-spy 的具体功能,以及如何使用它来解决实际的调试问题。

对于我而言,rxjs-spy 确实可以使调试 RxJS 变得有趣起来。

更多信息

rxjs-spy 的源码托管在 GitHub 上,这里有一个可以操作控制台 API 的在线示例。

还可以通过 NPM 来安装包。

本系列的下篇文章 —「 调试 RxJS 第2部分:日志篇 」

相关文章:

  • shell分割字符串函数(支持特殊字符和不可见字符)
  • JSP中动态include与静态include的区别介绍
  • 云计算网络基础第六天
  • Asp.Net中虚拟文件系统的使用
  • yum管理详解
  • mysql数据库操纵语言
  • Centos7系列(三)防火墙与网络区域详解
  • table的使用以及其内padding、margin的实现
  • 判断点是否在多边形内部
  • 算法系列——算法入门之递归分而治之思想的实现
  • 建立名称server
  • Hyper-V Server增强会话模式
  • 《Javascript高级程序设计 (第三版)》第五章 引用类型
  • Spring AOP 1
  • 如何启动Nunit的调试功能
  • 【跃迁之路】【585天】程序员高效学习方法论探索系列(实验阶段342-2018.09.13)...
  • C++类中的特殊成员函数
  • es6
  • JavaScript 一些 DOM 的知识点
  • JavaScript工作原理(五):深入了解WebSockets,HTTP/2和SSE,以及如何选择
  • Less 日常用法
  • Next.js之基础概念(二)
  • nfs客户端进程变D,延伸linux的lock
  • npx命令介绍
  • python 学习笔记 - Queue Pipes,进程间通讯
  • spring boot 整合mybatis 无法输出sql的问题
  • spring boot下thymeleaf全局静态变量配置
  • supervisor 永不挂掉的进程 安装以及使用
  • vue-router 实现分析
  • Vue学习第二天
  • 编写符合Python风格的对象
  • 不上全站https的网站你们就等着被恶心死吧
  • 关于Java中分层中遇到的一些问题
  • 基于Android乐音识别(2)
  • 每天10道Java面试题,跟我走,offer有!
  • 区块链共识机制优缺点对比都是什么
  • 一道闭包题引发的思考
  • 再谈express与koa的对比
  • UI设计初学者应该如何入门?
  • # Swust 12th acm 邀请赛# [ A ] A+B problem [题解]
  • #pragma 指令
  • $var=htmlencode(“‘);alert(‘2“); 的个人理解
  • (Matlab)遗传算法优化的BP神经网络实现回归预测
  • (二)基于wpr_simulation 的Ros机器人运动控制,gazebo仿真
  • (附源码)springboot“微印象”在线打印预约系统 毕业设计 061642
  • (论文阅读40-45)图像描述1
  • (每日持续更新)jdk api之StringBufferInputStream基础、应用、实战
  • (自用)learnOpenGL学习总结-高级OpenGL-抗锯齿
  • .form文件_一篇文章学会文件上传
  • .NET 读取 JSON格式的数据
  • .net 使用$.ajax实现从前台调用后台方法(包含静态方法和非静态方法调用)
  • .NET国产化改造探索(一)、VMware安装银河麒麟
  • .NET设计模式(8):适配器模式(Adapter Pattern)
  • .net下简单快捷的数值高低位切换
  • .pings勒索病毒的威胁:如何应对.pings勒索病毒的突袭?