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函數發送數據,否則啥也不干。

(未完待續)


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com