Promise 與 Rx

Promise這個概念在JS開發者裏面可謂是深入人心,主要用它來避免callback hell。

1. Promise顧名思義,提供的是一個允諾,這個允諾就是在調用then之後,它會在未來某個階段把異步函數執行的結果傳給then      裏的函數

    Rx不是允諾,它本質上還是由訂閱發佈模式印出來的,核心思想就是數據響應式。

    (源頭)數據產生者——一系列的變換/過濾/合併(操作)——數據消費者使用,數據消費者何時使用,完全取決於數據流何時能流下來

2.Promise需要調用then或catch才能夠執行,catch是then的另一種形式,調用then或者catch之後返回一個新的Promise,新的     Promise又可以被調用,因此可以做成無限的then鏈。

  Rx數據是否流出不取決於subscribe,一個observable在未被訂閱的時候也可以流出數據,在之後被訂閱後先前流出的數據無法被消費者查知的,所以Rx引入了一個lazy模式,允許數據緩存着知道被訂閱,但數據是否流出並不依賴subscribe。

observable被訂閱後並不是返回新的observable,而是返回一個subsciber,這樣可以取消訂閱,但是也導致了鏈式斷裂,所以不能像Promise一樣組成無線then鏈。

3.Promise數據是一次性流出的,因爲Promise內部維持着狀態,初始化的pending,轉成resolved或者rejected之後,狀態就不可逆轉了。

舉例說promise().then(A).then(B).then(C).catch(D),數據是順着鏈以此傳播,但是隻有一次,數據從A到B之後,A這個promise的狀態發生了改變,從pedding轉成了resolved,那麼它就不可能再產生內容了,所以這個promise已經不是活動性的了。

而Rx則不同,我們從Rx的接口就可以知道,它有onNext,onComplete和onError,onNext可以響應無數次,這也是符合我們對數據響應式的理解,數據在源頭被隔三差五的發出,只要源頭認爲沒有流盡(onComplete)或者出了問題(onError),那麼數據就可以不斷的流到響應者那邊。

舉例來說,我們響應一個按鈕的點擊事件,那麼我們可以把這個事件抽象爲一個數據流,只要按鈕還在,那麼我們就認爲它是可以產生數據的。

 Rx.Observable.fromEvent(btn, 'click')
   .map(() => input.value)
   .filter(text => !!text)
   .distinctUntilChanged()
   .flatMapLatest(Rx.Observable.fromPromise(fetch("/").then(res=>res.text()))
   .subscribe(value=>{
      input.value = value;
   });

再比如說定時器,我們可以把一個每三秒執行一項複雜的操作抽象成源頭是定時器的操作,比如這樣

Rx.Observable.timer(0,3000)
             .timeInterval()
             .flatMapLatest()
             .subscrbe(value=>{
             })
在這裏面,數據是流動的,是活的,而不是像Promise那樣交給下一個then之後,自己就死了,這種差別需要格外注意。

4. Promise用then鏈來處理數據,包括對數據進行過濾、合併、變換等操作,它沒有真正意義上的數據消費者,then鏈的每一個都是數據消費者,所以它非常適合組裝流程式,也就是說A完成之後做B,然後B完成後去完成C這種流程,這些流程可以無窮無盡,沒有底的。

Rx有數據產生的源頭和嚴格意義的數據消費者,數據可以在中間的操作符裏被處理,比如說做過濾,做合併,做節流,變換成新的數據源頭等等,可以把它想象成一個完整的數據鏈,有頭也有尾,到了最終消費者那邊這個數據流就算到底。

5. Promise的狀態發生改變後,我們如果再想重新從源頭開始的話,就需要在後續then鏈遞歸最開始的那一步,因爲後續者是新的promise,無法感知源頭的那個Promise。

而Rx的observable可以感知源頭,它有類似於retry、repeat這種重新開始的運算符,我們可以很方便的鏈式調用它,而不需要封裝成函數再遞歸。

6. Promise的then鏈裏面,每一行都是同樣的角色,也就是Promise,所以它既可以是源頭,也可以是數據處理者。

Rx這邊的observable還有一些變種,比如說常用的subject,它可以充當雙面角色,可以訂閱也可以發消息,這樣的話我們還可以用它來做很多封裝的工作。

所以Promise和Rx這兩個模式的思想差別很清晰,一個是流程式,一個是數據響應式,Promise可以用來貫串一連串單一的流程,而且這個流程是可以無限的,而Rx是用一個數據流來貫串所有操作符,它有一個真正意義上的數據消費者。

我們在哪些場景下用Rx比較方便?首先是需要源源不斷的流出數據的場景,因爲Promise是一次性的,不適合做這類工作。

比如說把事件/定時器抽象成Rx的Observable更合適,事件可以響應很多次,定時器也可以響應很多次,我們還可以利用Rx的debounce運算符來進行節流,在頻繁觸發事件的時候過濾那些重複的。

其次是可能需要重試的場景,由於Rx有retry或者repeat這種從源頭開始的運算符,我們可以用它來執行比如“出錯後重試三次”之類動作,而Promise就需要你遞歸處理了,破壞了then的鏈式。

而Promise也有一些優於Rx的場景,比如最開始我們舉例的那個結合generate function做的yield自動調用,Promise的鏈式是無窮的,所以適合這類流程式的工作,async關鍵字依賴Promise也是正確之舉。

還有就是那些一次性的工作,比如說我們請求http api,在得到數據之後response socket就會關閉,這個時候不會有第二次的數據流動,這就是一次性的數據流動,Promise就可以完成的很好。

這兩種模式都有自己的想法,所以在使用Rx的時候,不要把它當成Promise來用,記住它的本質是數據響應。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章