隨著互聯(lián)網(wǎng)行業(yè)的飛速發(fā)展,Web開發(fā)者面臨的挑戰(zhàn)也越來越大,伴隨著功能的增多,Web應(yīng)用的復(fù)雜度也快速膨脹。對于Web應(yīng)用,*復(fù)雜的問題就是異步操作的處理, 無論用戶操作、AJAX請求、動畫、WebSocket推送都涉及到異步操作,傳統(tǒng)的異步處理方法越來越不適應(yīng)復(fù)雜應(yīng)用的需要,RxJS的產(chǎn)生,就是為了解決日益復(fù)雜的前端異步處理問題。RxJS是一門進(jìn)入門檻比較高的技術(shù),本書的目的就是降低學(xué)習(xí)成本,由淺入深地介紹RxJS,逐步解析這門技術(shù)的各個(gè)方面,讓讀者能夠以一個(gè)平緩的學(xué)習(xí)曲線來掌握這一潛力無限的技術(shù)。
主要內(nèi)容包括:·函數(shù)響應(yīng)式編程·數(shù)據(jù)流的工作原理·RxJS操作符的創(chuàng)建方法·RxJS所有操作符的詳細(xì)分類介紹·多播的應(yīng)用·Scheduler的應(yīng)用·RxJS的調(diào)試和測試方法·React、Redux和RxJS的組合使用方法·RxJS開發(fā)游戲的實(shí)踐
本書系統(tǒng)講解RxJS響應(yīng)式編程的技術(shù)原理與應(yīng)用。第1章剖析函數(shù)響應(yīng)式編程的基本概念,通過簡單RxJS代碼引入函數(shù)響應(yīng)式編程,并與傳統(tǒng)編程方式對比,解釋這種編程范式的優(yōu)勢,以及這種范式形成的歷史。第2章介紹學(xué)習(xí)RxJS必須掌握的基本概念,包括數(shù)據(jù)流、操作符和觀察者模式。第3~9章介紹RxJS的各種操作符,以及如何選擇恰當(dāng)?shù)牟僮鞣麃硗瓿刹煌娜蝿?wù)。第10章介紹RxJS如何實(shí)現(xiàn)多播的方式。第11章介紹實(shí)現(xiàn)調(diào)度Scheduler的作用、原理與使用。第12章介紹如何調(diào)試和測試RxJS相關(guān)代碼,寫出高可測試性代碼。第13章介紹如何在React應(yīng)用中使用RxJS,提高代碼質(zhì)量。第14章介紹Redux與RxJS的組合應(yīng)用,發(fā)揮兩者的共同優(yōu)勢。第15章介紹一個(gè)綜合案例,用RxJS實(shí)現(xiàn)網(wǎng)頁游戲Breakout,并剖析RxJS如何實(shí)現(xiàn)動畫和繪圖。
軟件開發(fā)中有什么老問題?技術(shù)發(fā)展迅速,用戶的需求增加更快,軟件的代碼庫也會隨需求增長快速膨脹。
在這種情況下,如何保證代碼質(zhì)量?如何控制代碼的復(fù)雜度?如何維持代碼的可維護(hù)性?這些就成了軟件開發(fā)的大問題。業(yè)界的同仁們?yōu)榱私鉀Q這些老問題做了各種努力,函數(shù)式編程和響應(yīng)式編程就是在實(shí)踐中被證明行之有效的兩種方法。
RxJS兼具函數(shù)式和響應(yīng)式兩種編程方式的特點(diǎn),RxJS擅長處理異步操作,因?yàn)樗鼘?shù)據(jù)采用推的處理方式,當(dāng)一個(gè)數(shù)據(jù)產(chǎn)生的時(shí)候,被推送給對應(yīng)的處理函數(shù),這個(gè)處理函數(shù)不用關(guān)心數(shù)據(jù)是同步產(chǎn)生的還是異步產(chǎn)生的,這樣就把開發(fā)者從命令式異步處理的枷鎖中解放了出來。
本書由淺入深地講解RxJS,不僅介紹所有操作符的功能,而且講解實(shí)際應(yīng)用與利弊,是提升開發(fā)內(nèi)功的好教程。
Preface 前 言這是一個(gè)信息技術(shù)爆炸的時(shí)代,計(jì)算機(jī)編程語言和框架層出不窮,同時(shí),編程的風(fēng)格也在發(fā)生變化。也許你還沒有注意到,但是變化的確在發(fā)生。曾經(jīng)面向?qū)ο笫骄幊谭椒ㄒ唤y(tǒng)天下,如今越來越多開發(fā)者開始轉(zhuǎn)向函數(shù)式編程方法;與此同時(shí),一直具有統(tǒng)治地位的指令式編程方法,也發(fā)現(xiàn)自己要面對一個(gè)新的對手:響應(yīng)式編程。在這本書里,我們介紹的就是兼具函數(shù)式和響應(yīng)式兩種先進(jìn)編程風(fēng)格的框架RxJS。
RxJS是Reactive Extension這種模式的JavaScript語言實(shí)現(xiàn),通過學(xué)習(xí)了解RxJS,你將打開一扇通往全新編程風(fēng)格的大門。
當(dāng)然,我們學(xué)習(xí)RxJS,并不是因?yàn)镽xJS是一項(xiàng)炫酷的技術(shù),也不是因?yàn)镽xJS是一個(gè)最新的技術(shù)。在技術(shù)的道路上,如果只是追逐炫酷和最新,肯定是要吃苦頭的,因?yàn)檫@是舍本逐末。
我們學(xué)習(xí)和應(yīng)用RxJS,是因?yàn)镽xJS的的確確能夠幫助我們解決問題,而且這些問題長期以來一直在困擾我們,沒有好的解決辦法,這些問題包括:
如何控制大量代碼的復(fù)雜度;如何保持代碼可讀;如何處理異步操作。
RxJS的價(jià)值在于提供了一種不一樣的編程方式,能夠解決很多困擾我們開發(fā)者的問題。
打開了這本書的讀者,你們想必也曾經(jīng)面對過軟件開發(fā)過程中的這些挑戰(zhàn),學(xué)習(xí)RxJS能夠幫助大家在軍火庫中增加一種有力武器,也許你不用隨時(shí)隨地使用這種武器,但是,你肯定多了一種解決這些問題的更有效方法。
不過,可能你也早有耳聞,RxJS的學(xué)習(xí)曲線非常陡峭,可以說已經(jīng)陡峭到了不能稱為學(xué)習(xí)曲線的程度,應(yīng)該稱為學(xué)習(xí)懸崖。這并不夸張,我個(gè)人學(xué)習(xí)RxJS就嘗試了三次。
第一次學(xué)習(xí)RxJS時(shí),感覺這種思想很酷,但是很快就發(fā)現(xiàn)太多概念都是交叉出現(xiàn)的,文檔中為了解釋一個(gè)概念,就會引入一個(gè)新的概念,當(dāng)我去了解這個(gè)新的概念的時(shí)候,發(fā)現(xiàn)為了解釋這個(gè)新的概念又需要理解其他的概念,整個(gè)RxJS的知識圖就像是一個(gè)迷宮,我第一次學(xué)習(xí)RxJS的經(jīng)歷就終結(jié)在這個(gè)迷宮之中。
幾個(gè)月后,我第二次鼓起勇氣來學(xué)習(xí)RxJS,因?yàn)橛辛说谝淮蔚囊恍┗A(chǔ),這一次還比較順利,我把概念都掌握得差不多了,但是接下來面對的就是RxJS中大量的操作符,RxJS的應(yīng)用幾乎就是在選擇用哪種操作符合適。雖然我把RxJS的迷宮整個(gè)都摸了一遍,但是很多操作符我也沒有發(fā)現(xiàn)實(shí)際的應(yīng)用場景,所以這一次學(xué)習(xí)最后依然不了了之。
最后,終于有個(gè)機(jī)會,我需要用RxJS來解決實(shí)際的問題。這一次,因?yàn)榇嬖趯?shí)際應(yīng)用的驅(qū)動,我不得不深入去理解RxJS的內(nèi)在機(jī)制,揣摩一個(gè)操作符為什么要設(shè)計(jì)成這樣而不是另一個(gè)樣子,把自己擺在RxJS的角度來思考問題。我還是很幸運(yùn),這一次,終于對RxJS有了一個(gè)全面的認(rèn)識。
我終于體會到RxJS的卓越之處,我很興奮,希望這個(gè)工具能夠被更多人了解,于是我向朋友們介紹RxJS,有的朋友的確花了時(shí)間去學(xué)習(xí),但是,他們大多數(shù)最后依然放棄了。
怎么會這樣?簡單來說,是因?yàn)镽xJS的學(xué)習(xí)曲線太陡峭。
上圖就是對RxJS學(xué)習(xí)曲線的形象描述,一般知識的學(xué)習(xí)曲線像是一個(gè)小山坡,而RxJS的學(xué)習(xí)曲線就像是一個(gè)懸崖,而且這個(gè)懸崖有的部分的傾斜角度超過了90度!
怎么會這樣?這個(gè)問題我也思考了很久,回顧自己三次學(xué)習(xí)RxJS的過程,我發(fā)現(xiàn)了問題的所在,那就是,目前幾乎沒有一個(gè)像樣的用線性方法教授RxJS的教材。
RxJS是開源軟件,這個(gè)軟件經(jīng)過了很長時(shí)間的演進(jìn),代碼的確可圈可點(diǎn),但是其文檔實(shí)在算不上優(yōu)秀。RxJS的官方文檔內(nèi)容雖然不少,但是內(nèi)容太多,很多部分之間相互引用,并沒有一步一步告訴初學(xué)者該如何入門,初學(xué)者很容易(就像我第一次學(xué)習(xí)RxJS一樣)發(fā)現(xiàn)自己陷入一個(gè)巨大的、沒有頭緒的迷宮之中。
于是我想,既然RxJS是一個(gè)好東西,那為什么不用一種簡單易懂的方式來介紹這種技術(shù)呢?這也正是寫作本書的動因。
在這本書里,我會盡量用一種線性的方法來介紹RxJS的各個(gè)方面,讀者按照正常的、從前到后的順序來閱讀,不需要在各個(gè)知識點(diǎn)之間跳來跳去,這樣,當(dāng)讀者看到最后一頁的時(shí)候,應(yīng)該就能夠?qū)xJS有全面深刻的認(rèn)識了。
讀者可能會想,RxJS這樣一個(gè)復(fù)雜難懂的東西,我有必要去學(xué)嗎?如果你只是滿足于現(xiàn)狀,那真的不用去學(xué),不過,我前面也說過,當(dāng)今世界的變化和發(fā)展非常快,函數(shù)式編程在目前也許還不起眼,但是在未來可能會占統(tǒng)治地位,這個(gè)變化可能發(fā)生在明年,也可能發(fā)生在明晚。你肯定不希望當(dāng)變化發(fā)生的時(shí)候自己手足無措,所以,花一些時(shí)間來接觸這個(gè)面向未來的思想,對你絕對沒有壞處。
坦白地說,我并不相信每個(gè)讀者學(xué)習(xí)RxJS的經(jīng)歷都會一帆風(fēng)順,如果你真的能夠一次就學(xué)透RxJS,那你真的很可能是一個(gè)天才,記得一定要給我留言;如果你在學(xué)習(xí)中遇到一些挫折,請相信,你并不孤單,這本書的作者就經(jīng)歷了三次學(xué)習(xí)才真正學(xué)會RxJS,任何疑問都可以留言和我交流。
讓我們開始這段旅程吧!
本書的內(nèi)容本書以線性方式來介紹RxJS,所以建議讀者以順序的方式來閱讀本書,如果讀者覺得對某一個(gè)方面已經(jīng)十分了解,也可以跳過相關(guān)章節(jié),不過,還是希望讀者在時(shí)間允許的情況下閱讀全部內(nèi)容,你肯定會有新的體會。本書
程墨 架構(gòu)師,曾任職于摩托羅拉、雅虎和微軟,云鳥配送平臺聯(lián)合創(chuàng)始人,目前服務(wù)于美國視頻服務(wù)公司Hulu。
目錄 Contents
前言
第1章 函數(shù)響應(yīng)式編程1
1.1 一個(gè)簡單的RxJS例子1
1.2 函數(shù)式編程5
1.2.1 什么是函數(shù)式編程5
1.2.2 為什么函數(shù)式編程最近才崛起11
1.2.3 函數(shù)式編程和面向?qū)ο缶幊痰谋容^13
1.3 響應(yīng)式編程14
1.4 Reactive Extension15
1.5 RxJS是否是函數(shù)響應(yīng)式編程16
1.6 函數(shù)響應(yīng)式編程的優(yōu)勢17
1.7 本章小結(jié)18
第2章 RxJS入門19
2.1 RxJS的版本和運(yùn)行環(huán)境19
2.2 Observable和Observer24
2.2.1 觀察者模式24
2.2.2 迭代器模式25
2.2.3 創(chuàng)造Observable26
2.2.4 跨越時(shí)間的Observable28
2.2.5 永無止境的Observable29
2.2.6 Observable的完結(jié)30
2.2.7 Observable的出錯處理31
2.2.8 Observer的簡單形式32
2.3 退訂Observable33
2.4 Hot Observable和Cold Observable35
2.5 操作符簡介37
2.6 彈珠圖39
2.7 本章小結(jié)41
第3章 操作符基礎(chǔ)42
3.1 為什么要有操作符42
3.2 操作符的分類44
3.2.1 功能分類45
3.2.2 靜態(tài)和實(shí)例分類46
3.3 如何實(shí)現(xiàn)操作符49
3.3.1 操作符函數(shù)的實(shí)現(xiàn)49
3.3.2 關(guān)聯(lián)Observable53
3.3.3 改進(jìn)的操作符定義55
3.3.4 lettable/pipeable 操作符60
3.4 本章小結(jié)68
第4章 創(chuàng)建數(shù)據(jù)流69
4.1 創(chuàng)建類操作符70
4.2 創(chuàng)建同步數(shù)據(jù)流70
4.2.1 create:毫無神奇之處71
4.2.2 of:列舉數(shù)據(jù)71
4.2.3 range:指定范圍73
4.2.4 generate:循環(huán)創(chuàng)建74
4.2.5 repeat:重復(fù)數(shù)據(jù)的數(shù)據(jù)流75
4.2.6 三個(gè)極簡的操作符:empty、never和throw78
4.3 創(chuàng)建異步數(shù)據(jù)的Observable對象80
4.3.1 interval和timer:定時(shí)產(chǎn)生數(shù)據(jù)80
4.3.2 from:可把一切轉(zhuǎn)化為Observable82
4.3.3 fromPromise:異步處理的交接84
4.3.4 fromEvent85
4.3.5 fromEventPattern87
4.3.6 ajax88
4.3.7 repeatWhen89
4.3.8 defer91
4.4 本章小結(jié)92
第5章 合并數(shù)據(jù)流93
5.1 合并類操作符94
5.1.1 concat:首尾相連94
5.1.2 merge:先到先得快速通過96
5.1.3 zip:拉鏈?zhǔn)浇M合99
5.1.4 combineLatest:合并最后一個(gè)數(shù)據(jù)102
5.1.5 withLatestFrom109
5.1.6 解決glitch112
5.1.7 race:勝者通吃115
5.1.8 startWith115
5.1.9 forkJoin117
5.2 高階Observable118
5.2.1 高階Observable的意義119
5.2.2 操作高階Observable的合并類操作符120
5.2.3 進(jìn)化的高階Observable處理124
5.3 本章小結(jié)128
第6章 輔助類操作符129
6.1 數(shù)學(xué)類操作符129
6.1.1 count:統(tǒng)計(jì)數(shù)據(jù)個(gè)數(shù)130
6.1.2 max和min:最大最小值130
6.1.3 reduce:規(guī)約統(tǒng)計(jì)131
6.2 條件布爾類操作符133
6.2.1 every134
6.2.2 find和findIndex135
6.2.3 isEmpty137
6.2.4 defaultIfEmpty138
6.3 本章小結(jié)138
第7章 過濾數(shù)據(jù)流139
7.1 過濾類操作符的模式140
7.1.1 filter141
7.1.2 first142
7.1.3 last144
7.1.4 take一族操作符145
7.1.5 計(jì)時(shí)的點(diǎn)擊計(jì)數(shù)網(wǎng)頁程序150
7.1.6 skip151
7.1.7 skipWhile和skipUntil151
7.2 回壓控制152
7.2.1 throttle和debounce154
7.2.2 auditTime和audit164
7.2.3 sampleTime和sample166
7.2.4 根據(jù)數(shù)據(jù)序列做回壓控制168
7.3 其他過濾方式171
7.3.1 ignoreElements172
7.3.2 elementAt172
7.3.3 single173
7.4 本章小結(jié)173
第8章 轉(zhuǎn)化數(shù)據(jù)流174
8.1 轉(zhuǎn)化類操作符174
8.2 映射數(shù)據(jù)175
8.2.1 map176
8.2.2 mapTo177
8.2.3 pluck178
8.3 緩存窗口:無損回壓控制179
8.3.1 windowTime和bufferTime180
8.3.2 windowCount和bufferCount183
8.3.3 windowWhen和bufferWhen184
8.3.4 windowToggle和buffer-Toggle185
8.3.5 window和buffer186
8.4 高階的map188
8.4.1 concatMap189
8.4.2 mergeMap192
8.4.3 switchMap193
8.4.4 exhaustMap195
8.4.5 高階的MapTo195
8.4.6 expand196
8.5 數(shù)據(jù)分組196
8.6 累計(jì)數(shù)據(jù)200
8.6.1 scan200
8.6.2 mergeScan201
8.7 本章小結(jié)203
第9章 異常錯誤處理204
9.1 異常處理不可避免204
9.2 異常處理的難點(diǎn)206
9.2.1 try/catch只支持同步運(yùn)算207
9.2.2 回調(diào)函數(shù)的局限207
9.2.3 Promise的異常處理209
9.3 RxJS的異常處理212
9.3.1 catch214
9.3.2 retry216
9.3.3 retryWhen217
9.3.4 finally220
9.4 重試的本質(zhì)221
9.5 本章小結(jié)223
第10章 多播225
10.1 數(shù)據(jù)流的多播225
10.2 Hot和Cold數(shù)據(jù)流差異228
10.3 Subject230
10.3.1 兩面神Subject230
10.3.2 用Subject實(shí)現(xiàn)多播233
10.3.3 makeHot 操作符234
10.3.4 Subject不能重復(fù)使用235
10.3.5 Subject可以有多個(gè)上游237
10.3.6 Subject的錯誤處理239
10.4 支持多播的操作符241
10.4.1 multicast241
10.4.2 publish253
10.4.3 share255
10.5 高級多播功能257
10.5.1 publishLast和Async-Subject258
10.5.2 pubishReplay和Replay-Subject259
10.5.3 publishBehavior和BehaviorSubject262
10.6 本章小結(jié)263
第11章 掌握時(shí)間的Scheduler265
11.1 Schedu