RxJS的另外四種實現方式(一)——代碼最小的庫 頂 原

接上篇RxJS的另外四種實現方式(序)

起因

想到這個庫的原因,是看了callbag庫想到的,callbag庫的原理大家可以自己找資料瞭解,我就不多贅述,我只談談我的理解。callbag的設計思路是把消費者和生產者合併成一個,通過互相傳遞一個回調函數實現通訊。看過部分操作符實現原理的同學肯定覺得邏輯十分難解,因爲過多的回調使得你的腦回路不夠用了。我用了一些庫函數後,我意識到,其實不需要如此複雜的設計,爲什麼呢?請看下文

大同小異的callbag

callbag裏面有很多代碼是重複書寫的,原因很簡單,功能是確定的,如訂閱功能,這是必不可少的操作,下面我來比較一下我的庫的實現和callbag的實現。

對比實現生產者interval

先上callbag的源碼

const interval = period => (start, sink) => {
  if (start !== 0) return;
  let i = 0;
  const id = setInterval(() => {
    sink(1, i++);
  }, period);
  sink(0, t => {
    if (t === 2) clearInterval(id);
  });
};

export default interval;

說明一下

if(start!=0)return

這句話在callbag實現庫裏面隨處可見,我就是因爲這句話引起的思考,爲什麼每次都要重複寫呢? 當然是因爲這是一個生產者,只發送數據,不會去接受數據。

  sink(0, t => {
    if (t === 2) clearInterval(id);
  });

上面這段代碼其實是實現了一個取消訂閱功能,實現方法是向傳來的回調函數再傳回一個回調函數,估計讀者腦子要燒糊了。

上面這個interval可觀察對象的原型可以代表大多數的callbag的案例,那麼有沒有辦法用更爲簡潔的方式實現呢?

ShowTime

exports.interval = period => n => {
    let i = 0;
    const id = setInterval(() => n(i++), period)
    return () => clearInterval(id)
}

什麼,只有這麼幾行代碼嗎?,沒錯,這就是我認爲實現代碼最小的庫了,不服來戰。此代碼不僅小,性能好,還通俗易懂。當然我還是得稍微解釋一下要使得interval(1000)成爲一個地道的生產者,必須要實現可以訂閱,可以取消訂閱,以及可以得到生產者發出的數據(有些還需要得到complete和error事件,interval不會complete也不會error)

  • interval(1000)將得到一個函數n=>……,這個函數接受一個next函數用於發送數據
  • 調用interval(1000)這個高階函數等同於“訂閱”,此處是重點(代替了callbag中發送type爲0的行爲)
  • 返回的是一個dispose函數,即用於“取消訂閱”的功能(代替了callbag中傳回一個回調並在裏面接受type爲2的行爲)
  • 函數中調用了傳入的next函數n,即發送出去了數據

當然interval不會獨立工作,我們需要更多的操作符和觀察者使得庫來運作。

對比操作符filter

下面是callbag的實現

const filter = condition => source => (start, sink) => {
  if (start !== 0) return;
  let talkback;
  source(0, (t, d) => {
    if (t === 0) {
      talkback = d;
      sink(t, d);
    } else if (t === 1) {
      if (condition(d)) sink(t, d);
      else talkback(1);
    }
    else sink(t, d);
  });
};

module.exports = filter;

依然出現了

if(start!=0)return

沒錯,因爲filter只用於被訂閱,本身作爲數據響應者,有人說不對,filter需要對上一級的源做響應,沒錯,所以需要訂閱上一級的源,但傳入的不是自身,而是另一個回調函數來響應,否則就會有問題。核心代碼就一句,卻需要一大堆代碼來維持正常運行,我看不下去了。

ShowTime

exports.filter = f => source => (n, c) => source(d => f(d) && n(d), c)

What?就一行代碼?你沒看錯,你沒看錯,你沒看錯! 我來解釋一下,這一行代碼。filter是一個操作符,filter(d=>d>1)代表我只接受大於1的數據,這個將返回一個source=>……的函數,這個函數接受一個source作爲上一級數據源,可以是上文的interval(1000)這樣的生產者,也可以是其他操作符。所以

const obserable = filter(d => d > 1)(interval(1000))

你將得到一個(n,c)=>……的函數,這個就是可觀察者,你可以傳入next函數n,和complete函數c來進行“訂閱”了

const disposable = obserable(d => console.log('得到',d),err => console.log('完成'))//err代表有錯誤,這裏先不處理

你訂閱過後會得到一個函數disposable,用於“取消訂閱”

disposable()//取消訂閱

這個filter代表了最小庫的精髓:disposable可以從箭頭函數一路返回,在filter中是隱含的,無需顯示實現而代表complete的c函數也是直接透傳,無需更改。唯獨需要操作的就是next函數,需要向source傳一個新的next函數。當滿足條件時就向下一級的next函數發送數據,否則啥也不幹。

(未完待續)

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