30 天精通 RxJS (01):认识 RxJS

独立开发者4个月前 (09-28)RxJS系列60

RxJS 是笔者认為未来几年内会非常红的 Library,RxJS 提供了一套完整的非同步解决方案,让我们在面对各种非同步行為,不管是 Event, AJAX, 还是 Animation 等,我们都可以使用相同的 API (Application Programming Interface) 做开发。


这是【30天精通 RxJS】的 01 篇,如果还没看过 00 篇可以往这边走: 30 天精通 RxJS (00): 关於本系列文章


在网页的世界存取任何资源都是非同步(Async)的,比如说我们希望拿到一个档案,要先发送一个请求,然后必须等到档案回来,再执行对这个档案的操作。这就是一个非同步的行為,而随著网页需求的复杂化,我们所写的 JavaScript 就有各种针对非同步行為的写法,例如使用 callback 或是 Promise 物件甚至是新的语法糖 async/await —— 但随著应用需求愈来愈复杂,撰写非同步的程式码仍然非常困难。

非同步常见的问题

  • 竞态条件 (Race Condition)

  • 记忆体洩漏 (Memory Leak)

  • 复杂的状态 (Complex State)

  • 例外处理 (Exception Handling)

Race Condition

每当我们对同一个资源同时做多次的非同步存取时,就可能发生 Race Condition 的问题。比如说我们发了一个 Request 更新使用者资料,然后我们又立即发送另一个 Request 取得使用者资料,这时第一个 Request 和第二个 Request 先后顺序就会影响到最终接收到的结果不同,这就是 Race Condition。

Memory Leak

Memory Leak 是最常被大家忽略的一点。原因是在传统网站的行為,我们每次换页都是整页重刷,并重新执行 JavaScript,所以不太需要理会记忆体的问题!但是当我们希望将网站做得像应用程式时,这件事就变得很重要。例如做 SPA (Single Page Application) 网站时,我们是透过 JavaScript 来达到切换页面的内容,这时如果有对 DOM 註册监听事件,而没有在适当的时机点把监听的事件移除,就有可能造成 Memory Leak。比如说在 A 页面监听 body 的 scroll 事件,但页面切换时,没有把 scroll 的监听事件移除。

Complex State

当有非同步行為时,应用程式的状态就会变得非常复杂!比如说我们有一支付费用户才能播放的影片,首先可能要先抓取这部影片的资讯,接著我们要在播放时去验证使用者是否有权限播放,而使用者也有可能再按下播放后又立即按了取消,而这些都是非同步执行,这时就会各种复杂的状态需要处理。

Exception Handling

JavaScript 的 try/catch 可以捕捉同步的例外,但非同步的程式就没这麼容易,尤其当我们的非同步行為很复杂时,这个问题就愈加明显。

各种不同的 API

我们除了要面对非同步会遇到的各种问题外,还需要烦恼很多不同的 API

  • DOM Events

  • XMLHttpRequest

  • Fetch

  • WebSockets

  • Server Send Events

  • Service Worker

  • Node Stream

  • Timer

上面列的 API 都是非同步的,但他们都有各自的 API 及写法!如果我们使用 RxJS,上面所有的 API 都可以透过 RxJS 来处理,就能用同样的 API 操作 (RxJS 的 API)。


这裡我们举一个例子,假如我们想要监听点击事件(click event),但点击一次之后不再监听。

原生 JavaScript

var handler = (e) => {	
console.log(e);	
document.body.removeEventListener('click', handler); // 结束监听
}
// 註册监听
document.body.addEventListener('click', handler);

使用 Rx 大概的样子

Rx.Observable	.fromEvent(document.body, 'click') // 註冊監聽	.take(1) // 只取一次	.subscribe(console.log);

JSbin | JSFiddle (点击画面后会在 console 显示,记得打开 console 来看)

大致上能看得出来我们在使用 RxJS 后,不管是针对 DOM Event 还是上面列的各种 API 我们都可以透过 RxJS 的 API 来做资料操作,像是范例中用 take(n) 来设定只取一次,之后就释放记忆体。

说了这麼多,其实就是简单一句话

在面对日益复杂的问题,我们需要一个更好的解决方法。

RxJS 基本介绍

RxJS 是一套藉由 Observable sequences 来组合非同步行為和事件基础程序的 Library!


可以把 RxJS 想成处理 非同步行為 的 Lodash。


这也被称為 Functional Reactive Programming,更切确地说是指 Functional Programming 及 Reactive Programming 两个编程思想的结合。


RxJS 确实是 Functional Programming 跟 Reactive Programming 的结合,但能不能称為 Functional Reactive Programming (FRP) 一直有争议。


Rx 在官网上特别指出,有时这会被称為 FRP 但这其实是个“误称”。


简单说 FRP 是操作随著时间连续性改变的数值 而 Rx 则比较像是操作随著时间发出的离散数值,这个部份读者不用分得太细,因為 FRP 的定义及解释一直存在著歧异,也有眾多大神為此争论,如下


André Staltz:Rx 著名的推广者,也是 RxJS 5 主要贡献者之一,同时是 Cycle.js 的作者。Staltz 特别写了一篇文章解释為什麼 Rx 不能说是 FRP 但他仍然称其為 FRP。


Juan Gomez:曾在 Netflix 工作,目前任职於 Fitbit,经常出现在国外演讨会,主要写 Android。Juan Gomez 在 [Droidcon NYC 2015 的演讲](https://realm.io/news/droidcon-gomez-functional-reactive programming/)中特别提出他坚持称 Rx 為 FRP。


Evan Czaplicki:任职於 NoRedInk,Elm 的作者。Evan 在 StrangeLoop 2014 的演讲中,特别為现在各种 FRP 的不同解释做分类。


笔者自己的看法是比较偏向直接称 Rx 為 FRP,原因是这较為直觉(FP + RP = FRP),也比较不会对新手造成困惑,另外就是其他各种编程范式(包含 OOP, FP)其实都是想法的集合,而非严格的指南(Guideline),我们应该更宽鬆的看待 FRP 而不是给他一个严格的定义。

关于 Reactive Extension (Rx)

Rx 最早是由微软开发的 LinQ 扩展出来的开源专案,之后主要由社群的工程师贡献,有多种语言支援,也被许多科技公司所採用,如 Netflix, Trello, Github, Airbnb...等。

Rx 的相关资讯

  • 开源专案 (Apache 2.0 License)

  • 多种语言支持

  • JavaScript

  • Java

  • C#

  • Python

  • Ruby

  • ...(太多了列不完)

  • 官网

  • ~~微软目前最成功的开源专案~~

LinQ 唸做 Link,全名是 Language-Integrated Query,其功能很多元也非常强大;学 RxJS 可以不用会。

Functional Reactive Programming

Functional Reactive Programming 是一种编程范式(programming paradigm),白话就是一种写程式的方法论!举个例子,像 OOP 就是一种编程范式,OOP 告诉我们要使用物件的方式来思考问题,以及撰写程式。而 Functional Reactive Programming 其实涵盖了 Reactive Programming 及 Functional Programming 两种编程思想。

Functional Programming

Functional Programming 大部分的人应该多少都有接触过,这也是 Rx 学习过程中的重点之一,我们之后会花两天的篇幅来细讲 Functional Programming。 如果要用一句话来总结 Functional Programming,那就是 用 function 来思考我们的问题,以及撰写程式

在下一篇文章会更深入的讲解 Functional Programming

Reactive Programming

很多人一谈到 Reactive Programming 就会直接联想到是在讲 RxJS,但实际上 Reactive Programming 仍是一种编程范式,在不同的场景都有机会遇到,而非只存在於 RxJS,尤雨溪(Vue 的作者)就曾在 twitter 对此表达不满!

Reactive Programming 简单来说就是 当变数或资源发生变动时,由变数或资源自动告诉我发生变动了


这句话看似简单,其实背后隐含两件事


当发生变动 => 非同步:不知道什麼时候会发生变动,反正变动时要跟我说


由变数自动告知我 => 我不用写通知我的每一步程式码


由於最近很红的 Vue.js 底层就是用 Reactive Programming 的概念实作,让我能很好的举例,让大家理解什麼是 Reactive Programming!


当我们在使用 vue 开发时,只要一有绑定的变数发生改变,相关的变数及画面也会跟著变动,而开发者不需要写这其中如何通知发生变化的每一步程式码,只需要专注在发生变化时要做什麼事,这就是典型的 Reactive Programming (记得必须是由变数或资源主动告知!)


Vue.js 在做 two-ways data binding 是透过 ES5 definedProperty 的 getter/setter。每当变数发生变动时,就会执行 getter/setter 从而收集有改动的变数,这也被称為依赖收集。


Rx 基本上就是上述的两个观念的结合,这个部份读者在看完之后的文章,会有更深的体悟。



相关文章

30 天精通 RxJS (00):关于本系列文章

前言笔者从去年就一直想参加铁人赛 30 天,一方面是希望利用机会把自己的所学做一次整理,另一方面想训练自己组织文章的能力。去年的时候我想写 ECMAScript 2015,也准备了一段时间,结果没想到...

30 天精通 RxJS (02):Functional Programming 基本观念

Functional Programming 是 Rx 最重要的观念之一,基本上只要学会 FP 要上手 Rx 就不难了!Functional Programming 可以说是近年来的显学,各种新的函式...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。