RxJS入门
无论你是 JavaScript 新手还是经验丰富的开发者,你很可能都听说过 RxJS。
RxJS 是目前最流行的 JavaScript 库之一。本文将简要介绍 RxJS 的基本概念以及如何在应用程序中使用它。
历史
所以在开始之前,了解 RxJS 的历史会很有帮助。
这一切都始于响应式扩展(Reactive Extensions ,简称 ReactiveX)。ReactiveX 最初由Erik Meijer提出,是观察者模式的一种实现。在其开发之后,围绕 .NET 和 JavaScript 等主流语言,相继开发了一系列相关的编程库。
RxJS 是 JavaScript 的响应式扩展(Reactive Extensions)实现。RxJS项目最初由Matthew Podwysocki等人发起,是一个独立的开源项目。大约从 RxJS 版本 5 开始,Ben Lesh等人对项目进行了改进,使其成为如今的样子。
RxJS 库也使用函数式编程来实现用于管理事件序列(流)的操作符和函数。想要深入了解函数式编程,我强烈推荐观看 Russ Olsen 在 GOTO 2018 上的视频。
命令式与声明式
当人们讨论 RxJS 时,通常会提到命令式编程和声明式编程。
命令式代码指的是以特定方式编写的代码。这种代码需要你手动控制流程,类似于Promise 的工作方式。
声明式编程指的是使用已声明的函数来执行操作。这种编程方式依赖于能够定义事件流的“纯”函数。在 RxJS 中,这种编程方式体现在可观察对象和运算符上。
这些定义稍后会在本文中变得更加清晰,但在这里介绍一下也无妨。
可观测变量
解释 RxJS 时,最简单的方法通常是先展示代码。
大多数人通常都熟悉以下实现的 Promise:
没什么特别的,只是使用了标准的“resolve/reject”语法。Promise 完成后,输出信息会写入控制台。
现在请将此与以下内容进行比较:
哇!那是什么?那是 RxJS!如果你仔细观察,你会发现它采用了声明式编程实践,首先定义可观察对象,然后使用观察者中的不同钩子,例如 `on` next、error`on` 和 `on` complete。
我稍后会在本文中再次提到这个例子,但首先想介绍一下。
RxJS 的工作原理是什么?
因此,在开始学习 RxJS 之前,最好先了解一些定义:
- 可观测对象= 已定义的事件流
- 订阅= 代表事件的实际执行流程(发起订阅基本上就是“开启”执行)。
- 运算符= 是“纯”函数,可以调用带有订阅的流。它们有不同的形式,既可以创建流,也可以在可管道化的流中复制流。
- Subject是一个事件发射器,可用于多播。它们是特殊的,用于在程序中注入发射器。
- 调度器= 它们有助于处理并发,实际上是 RxJS 中更高级的主题。我只是为了完整性才在这里提及。
更多信息和示例请参阅此处的官方 RxJS 入门指南。
既然我们已经引入了这些词汇,现在就可以正式讨论我之前举的例子了。
可观测变量(再次)
现在让我们回到我之前展示的代码:
这是一个很好的例子,因为它展示了 Observable 的实现。
首先,你需要定义 Observable ,next然后complete,当我启动执行流程时,subscribe我会包含有关如何执行该流程的定义:
- next = 执行
console.log从流中返回的内容。 - error =
console.log如果流中发生错误,则执行此操作 - complete =
done执行完成后向控制台输出信息
这是直接定义可观察对象的一种方法。每个观察者都有三个钩子:`on` next、error`on` 和complete`on`,您可以使用它们来定义执行行为。
操作员
Observables 很棒,但 RxJS 还提供了运算符,使定义 Observables 变得更加容易。
操作员分为两种类型:
- 创建运算符= 具有预定义行为的已生成可观察对象
- 可管道操作符= 使用语法“.pipe”返回其他可观察对象的可观察对象
以下是一个创建运算符的实际应用:
这里我们使用of运算符按顺序发出 `a` 10、` 20b`、`c` 和 `d`的值30。这只是一个非常基础的例子,但它能让你了解如何使用它在流中发出一组值,而无需手动定义观察者钩子。
以下是一个管道运算符的实际应用示例:
所以这里的情况稍微复杂一些,但我相信你能弄明白。
- 我们使用之前提到的创建运算 符
of来生成值流a,,,bc - 接下来,我们将输出传递
of给管道运算符。mergeMap - 然后我们
mergeMap创建一个新的可观察对象并将其传递给interval - 然后,在延迟 1 秒后
interval获取输出和每个值。console.log
所以,这基本上创建了一个包含pipeable操作符的流程。原始源 Observable 用于创建一个带有附加逻辑的新 Observable。
更简单的理解方式是,stream这里定义了一个“a”。与数据流一起使用的每个管道都会增加价值。
更形象地理解管道运算符的方式是,将其比作水流经一组管道。每根管道都会为水增加价值,直到水流结束。
从下图可以直观地看到这一流程:
为了更直观地展示 RxJS 的流程图,许多人会使用 Marble Diagrams(大理石图)。它们乍一看可能有点复杂,但如果你按照 RxJS 指南学习如何阅读,它们会非常有帮助。我建议你查看此处操作符页面底部的 RxJS 指南。
订阅和内存泄漏
因此,开发者在使用 RxJS 时遇到的一个大挑战是订阅过程中的内存泄漏。
内存泄漏是指您忘记“取消订阅”某个数据流,而该进程仍在继续运行并消耗大量内存。内存泄漏会迅速耗尽浏览器内存,并降低应用程序的运行速度。
最佳解决方案是始终确保.unsubscribe为你的可观察对象设置一个接口。你也可以依赖框架中预置的机制,例如 Angular 的asyncpipe。
以下代码会造成内存泄漏:
这段代码很容易修复,只需添加一个setTimeout在设定的时间间隔后取消订阅流的函数,如下所示:
我之前在 RxJS DEVTO 博客上发表过一篇关于这个主题的文章,标题是“处理订阅的常用方法”,链接在此。强烈建议您有时间阅读一下。
高级主题
到目前为止,我们只介绍了一些基本的执行流程。如果将 RxJS 操作符组合起来,就可以用少量代码实现相当复杂的功能。
当你从单个 Observable 创建一组 Observable 时,就会遇到挑战。这被称为高阶 Observable。RxJS提供了一些操作符来帮助你简化这种情况,例如:
我不会在这里深入探讨高阶可观察对象,因为我认为这超出了入门文章的范围。不过,我强烈建议您查阅RxJS 指南中关于运算符的部分,其中对此有更详细的讨论。
我在RxJS DEVTO 博客文章“从 Promises 到 Observables”中也介绍了更高级的主题(点击此处阅读)。在那篇文章中,我详细讲解了如何设置一个scan操作符来合并多个 HTTP 调用。
我还建议阅读RxJS DEVTO 博客网站上的文章,其中有更多关于 Observable 的高级案例和讨论。
延伸阅读
我在这篇文章中只是浅尝辄止地介绍了 RxJS 的一些功能。网上还有很多优秀的资料和视频,提供了深入的讲解和示例。
我建议先看看这些视频:
- GOTO 2018 – 40 分钟函数式编程 – Russ Olsen
- RealTalk JavaScript 第 31 集:RxJS 魔法(Ben Lesh 主讲)
- Ng-Cruise – Ben Lesh 的 RxJS 实例讲解
- 从零开始创建 Observable(现场编码演示)—— Ben Lesh
闭幕致辞
希望这篇文章对你学习 RxJS 有所帮助。在本文中,我详细介绍了 RxJS 的概念以及在程序中使用它的方法。
感谢阅读这篇文章!欢迎在 Twitter 上关注我@AndrewEvans0102,也欢迎访问andrewevans.dev查看我的最新项目。
文章来源:https://dev.to/andrewevans0102/getting-started-with-rxjs-2dl0

