RxJS的另外四種實現方式(五)——使用生成器實現

weixin_34194087發表於2018-09-20

接上一篇RxJS的另外四種實現方式(四)——效能最高的庫(續)

js的生成器一般情況下使用場景很少,開發者接觸的不是很多。不瞭解的可以先行檢視js語法瞭解。

這裡把其中的執行順序圖解一下

呼叫方                                                                 資料來源
next(value)--------------------------------------->              開始執行生成器函式體
            <-------------------------------------------------yield value2
next(value3)--------------------------------------->
            <-------------------------------------------------yield value4
next(value5)--------------------------------------->
            <-------------------------------------------------return value6

以上是正常返回最後值的過程,也可以永遠不return,變成一個無限生成資料的過程。
另一種情況是提前終止

呼叫方                                                                 資料來源
next(value)--------------------------------------->              開始執行生成器函式體
            <-------------------------------------------------yield value2
next(value3)--------------------------------------->
            <-------------------------------------------------yield value4
return()--------------------------------------->

這種情況下相當於主動關閉生成器。
可以向資料來源的函式發出錯誤:

呼叫方                                                                 資料來源
next(value)--------------------------------------->              開始執行生成器函式體
            <-------------------------------------------------yield value2
next(value3)--------------------------------------->
            <-------------------------------------------------try catch
throw(err)

以上各種行為都可以對應Rx,那麼生成器和Rx的最大區別是什麼呢?

就是誰是主動方,誰是被動方。在生成器中,呼叫方是主動方,相當於主動pull資料,而Rx中,資料來源是主動方,相當於主動push資料。(這裡和Rx中的推拉模式有區別)

那麼如何使用生成器實現Rx呢?其實你估計已經想到了,就是反過來即可:

Observable                                                    Observer
next(value)--------------------------------------->              開始執行生成器函式體
            <-------------------------------------------------yield value2(得到返回值是value3)
next(value3)--------------------------------------->
            <-------------------------------------------------yield value4(返回值是value5)
next(value5)--------------------------------------->
            <-------------------------------------------------return value6()
done==true

於是我們就得到了由Observable主動推送過來的資料了。

我們還是以interval舉例

exports.interval = period => sink => {
    if (sink.next().done) return noop
    let i = 0;
    const id = setInterval(() => sink.next(i++).done && clearInterval(id), period)
    return () => clearInterval(id)
}

這裡傳入的sink就是迭代器例項,我們主動呼叫next傳送資料
這裡我們判斷了next函式的返回值裡面的done屬性,如果Observer主動取消訂閱了(在生成器函式裡面執行了return語句)那麼done就為true

下面是filter操作符:

function* _filter(sink, f) {
    for (let done = sink.next().done; !done;) {
        let x = yield 0
        if (x === _done) break
        if (f(x)) done = sink.next(x).done
    }
    sink.next(_done)
    sink.return()
}
exports.filter = f => source => sink => source(_filter(sink, f))

_done是一個Symbol,用來表示Observable的complete事件
_filter是一個生成器,呼叫它時傳入下一級的迭代器(Observer)
yeild 0 不斷獲取上一級的Observable的資料,一旦收到_done,立即跳出迴圈,並將_done傳入sink中。

最後是實現Subscriber

function* subscribe(n, e, c) {
    while (true) {
        try {
            let result = yield 0
            while (result !== _done) {
                if (n(result) === _done) return
                result = yield 0
            }
            c && c()
        } catch (err) {
            e && e(err)
        }
    }
}
exports.subscribe = subscribe

是一個死迴圈,直到收到_done,或者丟擲異常。
至此,我們的Rx的基本功能已經實現,由於生成器的效能較差,所以本人沒有花很多時間去完善各種操作符,只作為一種可以實現的方式展示出來。

下一篇我們介紹最後一種實現方法。

相關文章