這幾天學習下《算法》的排序章節, 具體見對排序的總結,想着作點東西,能將各類排序算法的排序過程使用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']
而後就應該排序算法進行工做了,這裏思考一下
對於第一點,咱們須要將排序算法封裝成一個自定義的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--------------------------