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

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

原由

想到這個庫的緣由,是看了callbag庫想到的,callbag庫的原理你們能夠本身找資料瞭解,我就很少贅述,我只談談個人理解。callbag的設計思路是把消費者和生產者合併成一個,經過互相傳遞一個回調函數實現通信。看過部分操做符實現原理的同窗確定以爲邏輯十分難解,由於過多的回調使得你的腦回路不夠用了。我用了一些庫函數後,我意識到,其實不須要如此複雜的設計,爲何呢?請看下文函數

大同小異的callbag

callbag裏面有不少代碼是重複書寫的,緣由很簡單,功能是肯定的,如訂閱功能,這是必不可少的操做,下面我來比較一下個人庫的實現和callbag的實現。性能

對比實現生產者interval

先上callbag的源碼ui

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實現庫裏面隨處可見,我就是由於這句話引發的思考,爲何每次都要重複寫呢?
固然是由於這是一個生產者,只發送數據,不會去接受數據。code

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

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

上面這個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)blog

  • 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函數發送數據,不然啥也不幹。

(未完待續)

相關文章
相關標籤/搜索