Rxjs入門實踐-各類排序算法排序過程的可視化展現

Rxjs入門實踐-各類排序算法排序過程的可視化展現

這幾天學習下《算法》的排序章節, 具體見對排序的總結,想着作點東西,能將各類排序算法的排序過程使用Rxjs經過可視化的方式展現出來,正好練系一下Rxjs的使用

本文不會太多介紹Rxjs的基本概念,重點介紹如何用響應式編程的思想來實現功能git

在線演示地址github

源碼算法

效果圖編程

需求

頁面中包括一個隨機生成300個數字的按鈕和、一個選擇不一樣排序算法的下拉列表和一個echart渲染的容器元素數組

點擊按鈕會隨機生成300個隨機數,同時頁面渲染出300個數的柱狀圖,而後選擇一種排序算法後,頁面開始展現排序過程,在排序過程當中若是咱們切換成另外一種排序算法,會中止當前算法的可視化展現,轉而開始新的排序算法的可視化展現異步

思路

要展現出排序算法在排序過程當中數組中數據的變化,咱們要按期保存一下排序過程當中當前數組的快照,而後經過echart展現當前數組的數據,重複這個過程直到排序完成,咱們也就有了表示排序過程的一個動畫展現學習

具體實現

在Rxjs中,一切皆是流,要實現這個功能,重要的是肯定好數據流,以及數據流在將來一段時間內的變化過程動畫

根據頁面,能夠清晰的肯定幾個數據流this

按鈕點擊操做生成的數據流spa

const createNumber$ = Rx.Observable.fromEvent(query('.numberCreator'), 'click')

切換下拉列表生成的數據流

const select$ = Rx.Observable.fromEvent(query('.sortTypes'), 'change')

點擊按鈕生成隨機數組並渲染echart圖表很顯然就用到map和do這兩個operator

createNumber$
    .map(e => {
        return numberCreator()
    })
    .do(nums => {
        const option = getOption(nums)
        echartInstance.setOption(option)
    })

切換下拉列表時咱們要獲得當前選擇的排序算法的一個標識

let currentType
select$
    .map(e => e.target)
    .map(x => x.options[x.selectedIndex].value)
    .map(type => {
        return {
            type,
            timer:1
        }
    })
    .do(x => {
        currentType = x.type
    })

下面是重點

只點擊按鈕或者只切換下拉頁面都不該該展現排序過程,只有當兩個事件流都觸發了,而且以後某一個再次觸發的時候纔會渲染排序過程的動畫,因此咱們須要combineLatest操做符,將兩個數據流合併成一個

const combine$=Rx.Observable.combineLatest(
    createNumber$,
    select$
)

如今在combine$數據流中咱們就有個隨機數組和排序類型

[Array[300],'1']

而後就應該排序算法進行工做了,這裏思考一下

  • [] 怎樣來生成咱們排序算法排序過程當中數據的快照?
  • [] 生成的數據快照何時讓echart來渲染?

對於第一點,咱們須要將排序算法封裝成一個自定義的operator,在排序過程當中不斷next() 數據快照,
到這裏咱們的數據流就變成能在將來一段時間內不斷生成新Value的一個數據流

Rx.Observable.prototype.sort = function () {
    const input = this
    return Rx.Observable.create((observer) => {
        input.subscribe((arr) => {
            const nums = clone(arr[0])
            const select = arr[1]
            const sortMethod = sortTypes[select.type]
            sortMethod(nums, function (arr) {
                observer.next({
                    nums: JSON.parse(JSON.stringify(arr)),
                    select
                })
            }, error => {
                observer.error(error)
            })
        }, )

    })
}

combine$.sort()

對於第二點,由於排序算法是很是快的,若是咱們subscibe sort()操做符產生的新值就開始渲染echart,頁面上是看不出動畫效果的,因此,咱們須要延遲echart渲染圖表的過程,咱們須要將sort()觸發的值轉變成一個異步的新事件流並打平到原數據流中

combine$
    .sort()
    .flatMap(obj => {
        return Rx.Observable.of(obj).delay(100 * obj.select.timer++)
    })

注意obj.select.timer++,對於sort()先後觸發的兩個值,爲了展現出echart渲染的動畫,咱們要給它們渲染的時間依次遞增

到這一步,咱們的單次功能就能正常進行了,但若是在一個排序動畫過程尚未結束,咱們又點擊了一個新的排序類型,則新舊兩次的還在序列中沒進行的渲染都會依次進行,干擾echart渲染的效果,因此在切換到新的類型時,咱們要過濾序列中的值。

combine$
    .sort()
    .flatMap(obj => {
        return Rx.Observable.of(obj).delay(100 * obj.select.timer++)
    })
    .filter(x => {
        return x.select.type == currentType
    })
    .do(x => {
        const option = getOption(x.nums)
        echartInstance.setOption(option)
    })
    .subscribe(() => { }, null, () => {
        console.log('complete')
    })

整個數據流序列

-createNumber$---------------------------------------------------------------------------------
 
   ---------------select$-------------------------------------------------------------------------
                             combineLatest()
   ---------------------------combine$------------------------------------------------------------
                              sort()
   ---------------------------v1       v2       v3       v4 .......v11      v22      v33----------
                                flatMap()
   ---------------------------delay1  delay2  delay3  delay4 ....delay11  delay22  delay33--------
                                 filter(currentType==type)
   ---------------------------delay1  delay2  delay11  delay22  delay33--------------------------
相關文章
相關標籤/搜索