可观察对象、响应式编程和遗憾
截至撰写本文时,我已经在RxJS 项目上工作了将近六年。刚开始的时候,我完全不知道自己在做什么(如果没有 Paul Taylor 和其他人,我肯定不可能发布那些最初的版本)。我还记得自己当时在 npm 上查看每周的下载量,就能算出其中到底有多少是我的。快进到今天,RxJS 和可观察对象已经变得非常流行……很多人喜欢,也有人讨厌,而我担心的是,大多数人都误解了它们。
可观察对象、响应式编程和 RxJS
我现在看到的一个大问题是,无论好坏,可观察对象现在都与 RxJS 紧密相关。回想起来,我真希望我们当初把Observable
原语单独发布成一个包,把操作符也放在另一个包里。
当这项工作开始时,我天真地乐观地认为它会进入 ECMAScript 标准,而 RxJS 只会“变成一堆辅助函数的集合”,就像我之前说的那样。但几年过去了,TC39 提案停滞不前。最终,世界是通过 RxJSObservable
了解它的。Observable
Observable 不是 RxJS。Observable 不需要“操作符”。它们是原语。是 的“对偶” Iterable
。一种简单的基于推送的类型。仅此而已。
响应式编程不一定是可观察的。响应式编程是一种范式或实践。它可以用函数、Promise 等来实现。本质上,如果你能将代码划分成函数,让它们在不知道任何来源的情况下对传入事件做出“反应”,那么恭喜你,你就是“响应式”的。
RxJS 是一个围绕可观察对象构建的函数库,而不是反过来。可观察对象可以独立于RxJS 而存在。它们也出现在其他库中,通常形式略有不同,但总体概念是相同的。Facebook的 Relay 有一个内部的可观察对象实现,与 RxJS 的实现惊人地相似。事实上,我已经记不清有多少次见过这样的抽象,它相当于一个接口,接受一个回调来处理多个值、一个错误或一个完成,并返回或使用某种取消语义。
遗憾
1. 庞大的 API
RxJS 5 从 RxJS 4 及更低版本继承了其庞大的API 接口。而 RxJS 4 及更低版本又从 RxNET 继承了 API,而且是很多年前的事了。许多 API 甚至有人认为“不必要”,但之所以存在,是因为“它们一直存在,而且必须一直存在”。RxJS 5 或许是我们在这个库的历史上唯一一次真正精简 API 的机会。我们确实做了一些,但可能还不够。庞大的 API 接口在社区中引发了困惑和不满。在我看来,这一切都是可以理解的。
2. RxJS 超越 Observable
Observable 本身从来就没有机会展现自己的光芒。在我看来,RxJS 真正的优势在于Observable
类型本身,而不是操作符。那些操作符只是些用来做一些很酷事情的花招。拥有一个像 RxJS 这样带有保证的惰性类型Observable
才是真正的亮点。
您Observable
将获得以下保证:
- 一旦完成、出错或取消订阅,您将不会再收到任何消息
- 已注册的拆卸操作必将发生。如果您完成、出错或取消订阅,则保证会清理资源。
- 统一的 API 可以表示各种各样的事物:事件、多个值、单个值、用户交互、流数据、同步值、异步值等等。
它的设计还有其他优点。但在我看来,这些才是最大的优点。
有些人觉得 RxJS 及其所有操作符都和可观察对象密不可分。这真是令人遗憾。RxJSObservable
很简单,一个非常简单的类型。而 RxJS 却因其庞大的 API 和奇怪的名称而变得复杂。
3. 我们从未真正概述过 RxJS 最适合哪些用户
免责声明:以上仅代表我个人对 RxJS/Observable 使用的看法,不代表 RxJS 核心团队的观点。您可以随意使用 RxJS 或其他任何库,只要您觉得合适即可。只要代码有效,您可以维护它,测试它,在我看来,它就是好代码。结束。
简而言之,一旦人们接触到 RxJS,它就是一项令人兴奋的技术。它会突然间被应用于方方面面。公平地说,这种心态在很多库和框架的技术中都存在。但我认为,对于 RxJS 来说,这种心态会变得阴险,损害 RxJS 社区的利益。
例子:
-
你有一个按钮,点击后会获取最新数据并显示出来。你需要完整的 RxJS 吗?不,可能不需要。“但是取消怎么办???” ……你想要一个可观察的,而不是操作符。你可以在这里使用 RxJS 来
Observable
实现,但我建议不要直接使用concatMap
et al 。尤其是在你的团队不习惯使用 RxJS 的情况下。但这并不意味着你不应该使用Observable
。事实上,你或许应该使用 。 -
你有通过 Web Socket 传输的流数据,你需要将其拆分成几个不同的流,并更新 UI 的两个部分。没错!这就是 RxJS 的用途。你
filter
离一个可靠的用例只差一个操作符了。 -
即使使用返回 Promise 的 API,您也遇到了复杂的异步协调和/或竞争条件?说实话,您可能也想在这里使用 RxJS,因为它提供了
Observable
、以及类似的实用运算符concatMap
,可以保证顺序等,并且与和具有完整的互操作性。async/await
Promise
4. 我们从未教过人们如何使用 RxJS 编写可读的代码
我们把强大的工具交给人们,然后让他们自己动手。没有提供任何指导或经验,教他们如何有效地使用图书馆,以免把同事逼疯。这就像拿到一套没有说明书的电动工具。你该如何保养它?如何解决问题?工具该放在哪里?等等。
这样做的结果是,人们写的代码在重新审视时变得难以理解。最令人吃惊的是,一些工程师,他们通常都很理性,却宣称 RxJS “不可读”,就好像无论他们怎么努力,都无法让代码变得可读一样。在我看来,这简直是失败主义。和其他任何事情一样,阅读和组织 RxJS 代码的良好实践和策略是可以学习和教授的。但我知道,我个人在传播这方面的知识方面做得还不够。
结果
总体而言,我认为大家对 RxJS 的反响非常积极。社区已经组织了一次会议。我看到很多社区(不仅仅是 Angular)都在讨论它。而且它的使用率也在稳步增长。
但反过来想,RxJS 和 Observable 的声誉也受到了损害,这源于人们对 Observable 和 RxJS 的误解,以及对这个库的普遍滥用,在我看来。一些知名科技人士甚至高呼“真希望 RxJS 不存在”。我担心,如果这种想法蔓延开来,RxJSObservable
本身就会走向毁灭。说实话,这将是最大的耻辱。
本身Observable
就是一个巨大的胜利。正如我上面所说,它是一个原语,以多种形式出现在许多地方,我认为它应该在语言中占有一席之地,就像Iterable
和 一样Promise
。在我看来,人们对 RxJS 的 API 感到厌恶和/或滥用和误用是完全可以理解的。
RxJS 的某些部分我并不喜欢,而且我无法快速调整这个库,因为它太流行了,很容易让太多人失望。但我最喜欢的部分,也就是 RxJSObservable
本身,以及它提供的保证,却面临着被某些人弃之不顾的危险。在我看来,这真是太悲惨了。
前进的道路
就我而言,我计划继续倡导促进人们对 RxJS 和 Observable 的“何时/何地/为何”的理解。我希望更好地区分 Observable 和 RxJS。我还想努力简化 RxJS API:精简 API,移除不必要的部分,改进文档和可读性,为用户提供更多关于如何使代码更易于维护的指导等等。
别误会,我对 RxJS 目前的情况还有其他遗憾,但我相信随着时间的推移,我们能够弥补所有这些不足。我最担心的是,仍然有很多人不了解 RxJS 的Observable
原始特性及其优势,因为他们把它和 RxJS 联系在一起,并且由于学习难度较高而不愿尝试。