RxJS是一個JavaScript庫,用來編寫異步和基於事件的程序。RxJS結合了觀察者模式、迭代器模式和使用集合的函數式編程,以知足以一種理想方式來管理事件序列所須要的一切。前端
能夠把RxJS看成用來處理事件的 Lodash。
在如今的Web開發中,異步(Async)操做隨處可見,好比使用ajax提交一個表單數據,咱們須要等待服務端返回提交結果後執行後續操做,這就是一個典型的異步操做。雖然JavaScript爲了方便開發者進行異步操做,提出了不少解決方案(callback,Promise,Async/await等等),可是隨着需求越發複雜,如何優雅的管理異步操做仍然是個難題。vue
此外,異步操做API千奇百怪,五花八門:ajax
以上這些經常使用的API所有都是異步的,可是每一個使用起來卻徹底不一樣,無形中給開發者增長了很大的學習和記憶成本。npm
使用RxJS能夠很好的幫助咱們解決上面兩個問題,控制大量異步代碼的複雜度,保持代碼可讀性,並統一API。編程
舉個栗子:頁面上有一個搜索框,用戶能夠輸入文本進行搜索,搜索時要向服務端發送異步請求,爲了減少服務端壓力,前端須要控制請求頻率,1秒最多發送5次請求,而且輸入爲空時不發送請求,最後將搜索的結果顯示在頁面上。segmentfault
一般咱們的作法是這樣的,先判斷輸入是否爲空,若是不爲空,則構造一個截流函數來控制請求頻率,這其中涉及到建立和銷燬定時器,此外,因爲每一個請求返回時間不肯定,如何獲取最後一次搜索結果,須要構造一個棧來保存請求順序, 想完美實現需求並不簡單。設計模式
RxJS是如何解決這個問題的呢?請看下面的代碼:數組
// 1.獲取dom元素 const typingInput = document.querySelector("#typing-input"); // 輸入 const typingBack = document.querySelector("#typing-back"); // 輸出 // 2.模擬異步請求 const getData = value => new Promise(resolve => setTimeout(() => resolve(`async data ${value}`), 1000) ); // 3.RxJS操做 const source$ = fromEvent(typingInput, "input") // 建立事件數據流 .pipe( // 管道式操做 map(e => e.target.value), // 獲取輸入的數據 filter(i => i), // 過濾空數據 debounceTime(200), // 控制頻率 switchMap(getData) // 轉化數據爲請求 ); // 4.輸入結果 source$.subscribe(asyncData => (typingBack.innerHTML = asyncData));
這就是所有代碼,也許有些地方看不太懂 ,不要緊,先不要着急,咱們分步解讀一下。網絡
注意,這段代碼咱們使用的所有變量都是用const聲明的,所有是不可變的,也便是變量聲明時是什麼值,就永遠是什麼值,就像定義函數同樣。相對於傳統的指令式編程,RxJS的代碼就是由一個一個不可變的函數組成,每一個函數只是對輸入參數做出相應,而後返回結果,這樣的代碼寫起來更加清爽,也更好維護。前端工程師
RxJS結合了函數式和響應式這兩種編程思想,爲了更深刻的瞭解RxJS,先來介紹一下什麼是函數式編程和響應式編程。
函數式編程(Functional Porgramming)是一種編程範式,就像「面向對象編程」同樣,是一種編寫代碼的「方法論」,告訴咱們應該如何思考和解決問題。不一樣於面向對象編程,函數式編程強調使用函數來解決問題。
這裏有兩個問題:
與之對應的是命令式編程,也是最多見的編程模式。
舉個例子,咱們但願寫個函數,把數組中的每一個元素乘以2,使用命令式編程,大概是這個樣子的:
function double(arr) { const result = [] for(let i=0,l=arr.length;i<l;i++) { result.push(arr[i] * 2) } return result }
咱們將整個邏輯過程完整描述了一遍,完美。
但若是又來了一個新需求,實現一個新函數,把數組中每一個元素加1,簡單,再來一遍:
function addOne(arr) { const result = [] for(let i=0,l=arr.length;i<l;i++) { result.push(arr[i] + 1) } return result }
是否是感受哪裏不對?double和addOne百分之九十的代碼徹底同樣,「重複的代碼是萬惡之源。」咱們應該想辦法改進一下。
這裏就體現了命令式編程的一個問題,程序按照邏輯過程來執行,可是不少問題都有類似的模式,好比上面的double和addOne。很天然咱們想把這個模式抽象一下,減小重複代碼。
接下來咱們使用JavaScript的map函數來重寫double和addOne:
function double(arr) { return arr.map(function(item) { return item * 2 }) } function addOne(arr) { return arr.map(function(item) { return item + 1 }) }
重複代碼所有被封裝到map函數中。而咱們須要作的只是告訴map函數應該如何映射數據,這就是聲明式編程。相比較以前的代碼,這樣的代碼更容易維護。
若是使用箭頭函數,代碼還能夠進一步簡化:
const double = arr => arr.map(item => item * 2) const addOne = arr => arr.map(item => item + 1)
注意以上兩個函數的返回結果都是一個新的數組,而並無對原數組進行修改,這符合函數式編程的另一個要求:純函數。
純函數是指知足如下兩個條件的函數:
舉個栗子:
const arr = [1, 2, 3, 4, 5] arr.slice(0, 3) // [1, 2, 3] arr.slice(0, 3) // [1, 2, 3] arr.slice(0, 3) // [1, 2, 3]
JavaScript中數組的slice方法無論執行幾回,返回值都相同,而且沒有改變任何外部狀態,因此slice就是一個純函數。
const arr = [1, 2, 3, 4, 5] arr.splice(0, 3) // [1, 2, 3] arr.splice(0, 3) // [4, 5] arr.slice(0, 3) // []
相反,splice方法每次調用的結果就不一樣,由於splice方法改變了全局變量arr的值,因此splice就不是純函數。
不純的函數每每會產生一些反作用(Side Effect),好比如下這些:
使用純函數能夠大大加強代碼的可維護性,由於固定輸入老是返回固定輸出,因此更容易寫單元測試,也就更不容易產生bug。
數據不可變是函數式編程中十分重要的一個概念,意思是若是咱們想改變一個變量的值,不是直接對這個變量進行修改,而是經過調用函數,產生一個新的變量。
若是你是一個前端工程師,確定已經對數據不可變的好處深有體會。在JavaScript中,字符串(String),數字(Number)這兩種類型就是不可變的,使用他們的時候每每不容易出錯,而數組(Array)類型就是可變的, 使用數組的pop、push等方法都會改變原數組對象,從而引起各類bug。
注意,雖然ES6已經提出了使用const聲明一個常量(不可變數據),可是這隻能保證聲明的對象的引用不可改變,而這個對象自身仍然能夠變化。好比用const聲明一個數組,使用push方法仍然能夠像數組中添加元素。
和麪向對象編程相比,面向對象編程更傾向把狀態的改變封裝到對象內部,以此讓代碼更清晰。而函數式編程傾向數據和函數分離,函數能夠處理數據,但不改變原數據,而是經過產生新數據的方式做爲運算結果,以此來儘可能減小變化的部分,讓咱們的代碼更清晰。
和函數式編程相似,響應式編程(Reactive Programming)也是一種編程的範式。從設計模式的角度來講,響應式編程就是「觀察者模式」的一種有效實踐。簡單來講,響應式編程指當數據發生變化時,主動通知數據的使用者這個變化。
不少同窗都使用過vue框架開發,vue中很出名的數據雙向綁定就是基於響應式編程的設計思想實現的。當咱們在經過v-bind綁定一個數據到組件上之後,無論這個數據什麼時候發生變化,都會主動通知綁定過的組件,使咱們開發時能夠專一處理數據自己,而不用關心如何同步數據。
而在相應時編程裏最出名的框架就是微軟開發的Reactive Extension。這套框架旨在幫助開發者解決複雜的異步處理問題。咱們的主角RxJS就是這個框架的JS版本。
安裝
npm install rxjs
導入
import Rx from "rxjs";
請注意,這樣導入會將整個RxJS庫所有導入進來,而實際項目未必會用上Rxjs的所有功能,所有導入會讓項目打包後變得很是大,咱們推薦使用深鏈(deep link)的方式導入Rxjs,只導入用的上的功能,好比咱們要使用Observable類,就只導入它:
import { Observable } from "rxjs/Observable";
實際項目中,按需導入是一個好辦法,可是若是每一個文件都寫一堆import語句,那就太麻煩了。因此,更好的實踐是用一個文件專門導入RxJS相關功能,其餘文件再導入這個文件,把RxJS導入工做集中管理。
篇幅有限,下一講將會講解RxJS中幾個核心概念,歡迎各位留言拍磚~