这是[30 天精通 RxJS] 的 01 篇, 如果还没看过 00 篇可以往这边走:
30 天精通 RxJS (00): 关于本系列文章 http://ithelp.ithome.com.tw/articles/10186103
在网页的世界存取任何资源都是非同步 (Async) 的, 比如说我们希望拿到一个档案, 要先发送一个请求, 然后必须等到档案回来, 再执行对这个档案的操作. 这就是一个非同步的行为, 而随著网页需求的複杂化, 我们所写的 JavaScript 就有各种针对非同步行为的写法, 例如使用 callback 或是 Promise 物件甚至是新的语法糖 async/await -- 但随著应用需求愈来愈複杂, 撰写非同步的程式码仍然非常困难.
非同步常见的问题
竞态条件 (Race Condition)
记忆体洩漏 (Memory Leak)
複杂的状态 (Complex State)
例外处理 (Exception Handling)
Race Condition https://goo.gl/GlNLYl
每当我们对同一个资源同时做多次的非同步存取时, 就可能发生 Race Condition 的问题. 比如说我们发了一个 Request 更新使用者资料, 然后我们又立即发送另一个 Request 取得使用者资料, 这时第一个 Request 和第二个 Request 先后顺序就会影响到最终接收到的结果不同, 这就是 Race Condition.
Memory Leak https://en.wikipedia.org/wiki/Memory_leak
Memory Leak 是最常被大家忽略的一点. 原因是在传统网站的行为, 我们每次换页都是整页重刷, 并重新执行 JavaScript, 所以不太需要理会记忆体的问题! 但是当我们希望将网站做得像应用程式时, 这件事就变得很重要. 例如做 SPA https://en.wikipedia.org/wiki/Single-page_application (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 https://jsbin.com/vofaluv/4/edit?console,output | JSFiddle https://jsfiddle.net/s6323859/d95a8peo/1/
- (点击画面后会在 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 在官网 http://reactivex.io/intro.html 上特别指出, 有时这会被称为 FRP 但这其实是个 "误称".
简单说 FRP 是操作随著时间连续性改变的数值 而 Rx 则比较像是操作随著时间发出的离散数值, 这个部份读者不用分得太细, 因为 FRP 的定义及解释一直存在著歧异, 也有众多大神为此争论, 如下
André Staltz https://medium.com/@andrestaltz/why-i-cannot-say-frp-but-i-just-did-d5ffaa23973b#.dhmsyic9w :Rx 著名的推广者, 也是 RxJS 5 主要贡献者之一, 同时是 Cycle.js 的作者. Staltz 特别写了一篇文章 https://medium.com/@andrestaltz/why-i-cannot-say-frp-but-i-just-did-d5ffaa23973b#.dhmsyic9w 解释为什麽 Rx 不能说是 FRP 但他仍然称其为 FRP.
Juan Gomez https://twitter.com/_juandg : 曾在 Netflix 工作, 目前任职于 Fitbit, 经常出现在国外演讨会, 主要写 Android.Juan Gomez 在 Droidcon NYC 2015 的演讲 https://realm.io/news/droidcon-gomez-functional-reactive-programming/ 中特别提出他坚持称 Rx 为 FRP.
Evan Czaplicki https://twitter.com/czaplic : 任职于 NoRedInk,Elm 的作者. Evan 在 StrangeLoop 2014 的演讲 https://www.youtube.com/watch?v=Agu6jipKfYw 中, 特别为现在各种 FRP 的不同解释做分类.
笔者自己的看法是比较偏向直接称 Rx 为 FRP, 原因是这较为直觉 (FP + RP = FRP), 也比较不会对新手造成困惑, 另外就是其他各种编程范式(包含 OOP, FP) 其实都是想法的集合, 而非严格的指南(Guideline), 我们应该更宽鬆的看待 FRP 而不是给他一个严格的定义.
关于 Reactive Extension (Rx)
Rx 最早是由微软开发的 LinQ 扩展出来的开源专案, 之后主要由社群的工程师贡献, 有多种语言支援, 也被许多科技公司所採用, 如 Netflix, Trello, Github, Airbnb... 等.
来源: http://www.jianshu.com/p/a3b3640ef7b3