RxJS可以解決的問題
- 我們知道傳統(tǒng)的for,while對(duì)循環(huán)體中的異步程序是無(wú)法感知的,或者說(shuō),它們不會(huì)等待異步程序執(zhí)行完畢再進(jìn)入下一輪循環(huán)。
- 錯(cuò)誤處理是任何程序都要解決的問題,本身就已很復(fù)雜的回調(diào)函數(shù)中再嵌入try/catch塊嗎?如果還想加入重試機(jī)制呢?
- 商業(yè)邏輯內(nèi)嵌在回調(diào)函數(shù)中,可讀性差,復(fù)雜度高。現(xiàn)如今流行的組件化編程,即可重用,又可解耦,還方便測(cè)試;
- 閉包是強(qiáng)大的,過度地使用閉包將導(dǎo)致我們不得不謹(jǐn)慎地審視變量的作用域以及其值。再加上共享變量帶來(lái)的副作用,混雜在if/else條件語(yǔ)句和for循環(huán)中,每天都會(huì)有修不完的bug;
- 根據(jù)事件或耗時(shí)操作無(wú)響應(yīng)的時(shí)間進(jìn)行取消操作;
- 自己實(shí)現(xiàn)throttling和debouncing是很困難的(二者區(qū)別見http://www.lxweimin.com/p/e91775195608)
- 眾所周知的事件監(jiān)聽?zhēng)?lái)的內(nèi)存泄露問題;
RxJS可以優(yōu)雅地替代callback,或者基于promises的任何第三方庫(kù),使我們可以使用一個(gè)編程模型來(lái)對(duì)待任何數(shù)據(jù)源(除了遠(yuǎn)程http請(qǐng)求,Node.js中的Event Emitter也使用的是回調(diào)機(jī)制)。也就是說(shuō),我們可以用RxJS來(lái)處理讀取文件,http請(qǐng)求,鼠標(biāo)點(diǎn)擊,鼠標(biāo)移動(dòng)這些事件。
數(shù)據(jù)的流動(dòng)和傳播
事件必然伴隨著數(shù)據(jù)的產(chǎn)生,在響應(yīng)式編程的世界中,我們把任何可以被使用的數(shù)據(jù)源統(tǒng)稱為流(Stream)。我們?cè)賮?lái)看一下什么是響應(yīng)式編程模型:
var x = 1;
var y = 2;
var z = x + y; // z = ?
x = 2; // z = ?
顯而易見,z第一次出現(xiàn)時(shí)的值是3,當(dāng)x重新被賦值為2時(shí),z的值是多少?顯而易見,z沒有變化,因?yàn)閍的變化不會(huì)引起z的變化。我們可以說(shuō)這種變化沒有被傳播開來(lái)。因此我們可以說(shuō),響應(yīng)式編程是圍繞數(shù)據(jù)的流動(dòng)和傳播的,某個(gè)變量的變化會(huì)導(dǎo)致其他變量的變化。RxJS就是用Javascript實(shí)現(xiàn)的響應(yīng)式編程。
RxJS官網(wǎng):https://github.com/ReactiveX/rxjs
reactive官網(wǎng):http://reactivex.io/
pipeline
pipeline的中文意思是管道,很形象。在管道里是一個(gè)接一個(gè)的函數(shù),管道的左邊接到Stream上,即數(shù)據(jù)源,數(shù)據(jù)將會(huì)流經(jīng)管道,并按順序作為參數(shù)傳遞給每一個(gè)函數(shù)。管道的右邊連接著的是最終使用數(shù)據(jù)的程序,我們稱之為數(shù)據(jù)消費(fèi)者。
萬(wàn)物皆stream
就像面向?qū)ο笾腥f(wàn)物皆對(duì)象一樣,在RxJS中,不管是單個(gè)值,字節(jié)流還是從http請(qǐng)求獲取來(lái)的值,都是stream。比如,假設(shè)在RxJS中我們有叫做Stream的數(shù)據(jù)類型,我們創(chuàng)建了一個(gè)單個(gè)值的數(shù)據(jù)源:
Stream(2017);
這個(gè)時(shí)刻,它僅僅是個(gè)數(shù)據(jù)源,什么行為也沒有發(fā)生,如果想讓它運(yùn)作起來(lái),我們得需要消費(fèi)者或者叫做觀察者。通過消費(fèi)者的訂閱行為,數(shù)據(jù)源中的數(shù)據(jù)才開始真正流動(dòng)起來(lái)。因此我們可以知道stream這個(gè)數(shù)據(jù)類型是惰性求值的,不像promises,一旦創(chuàng)建即開始運(yùn)行。下面看看如果訂閱它讓它流動(dòng)起來(lái):
Stream(2017).subscribe(
result => {
console.log(result);
}
);
subscribe函數(shù)中的箭頭函數(shù)即消費(fèi)者,當(dāng)消費(fèi)者一接收到數(shù)據(jù),stream就結(jié)束了。
有管道的例子:
Stream(1, 2, 3, 4)
.filter(val => (val % 2) === 0)
.map(val => val * val)
.subscribe(
result => {
console.log(result);
}
);
filter和map即管道中的函數(shù),它們中的val參數(shù)即從stream中流出的數(shù)據(jù)。它們一起組成了pipeline。
RxJS中的組件
生產(chǎn)者:在RxJS中的生產(chǎn)者叫做Observables。Observables負(fù)責(zé)推送事件,但不處理事件;
-
消費(fèi)者:在RxJS中的消費(fèi)者叫做Observer。
數(shù)據(jù)只會(huì)從生產(chǎn)者流向消費(fèi)者
管道:在RxJS中,管道中的一個(gè)一個(gè)函數(shù)叫做observable operators,簡(jiǎn)稱operators。
時(shí)間:我們知道異步程序不容易處理的背后實(shí)質(zhì)就是時(shí)間問題,RxJS是面向異步編程的解決方案,因此時(shí)間遍布于RxJS中的每一個(gè)角落。目前為止,我們只需要了解時(shí)間在RxJS中不是恒定的,產(chǎn)生事件的快慢與否取決于我們的需求。當(dāng)然,RxJS給了我們解決方案。
響應(yīng)式編程范式與其他編程范式
面向?qū)ο缶幊桃?code>狀態(tài)為中心,函數(shù)式編程以行為
為中心,而響應(yīng)式編程則需要我們把數(shù)據(jù)看做是改變并流動(dòng)著的。在這里我想說(shuō)的是,RxJS可以很好的和其他范式配合起來(lái)使用。我們可以用面向?qū)ο蠓妒絹?lái)構(gòu)建我們的模型,用函數(shù)式編程和響應(yīng)式編程來(lái)構(gòu)建行為和處理事件。
在面向?qū)ο缶幊讨校瑺顟B(tài)是保存在變量或者集合對(duì)象里的。而響應(yīng)式編程中的狀態(tài)是短暫的、瞬間的
,也就是說(shuō)在Rx中是永遠(yuǎn)不保留狀態(tài)的。
面向?qū)ο缶幊淌堑湫偷拿钍骄幊蹋憫?yīng)式編程則鼓勵(lì)我們寫聲明式的程序,也就是表達(dá)做什么,而不是表達(dá)怎么做。RxJS從函數(shù)式編程中借鑒了這一點(diǎn)。