參加 2018 ngChina 開發者大會,特別喜歡 Michael Hladky 奧地利帥哥的 RxJS 分享,如今拿出來好好學習工做坊的內容(工做坊Demo地址),結合這個示例,作了一個改進版本,實現更簡潔,邏輯更直觀。
瞭解者可跳過次章節
摩斯密碼(Morse),是一種時通時斷的信號代碼,這種信號代碼經過不一樣的排列組合來表達不一樣的英文字母、數字和標點符號等。 數組
地球人都知道的 SOS 求救信號,就是 Morse,三短(S) 三長(O) 三短(S)。 緩存
信號對應表以下: bash
分析關鍵步驟,很巧,和把大象裝進冰箱裏一樣都只須要三步耶:less
第一步,識別點信號,短爲 「滴」 長爲「嗒」。 函數
第二步,根據 「長間隔」 來切片分組。 學習
第三步,分組數據根據對應錶轉化出最終結果。 優化
開始前要作好熱身活動: ui
Morse 的最小單元,"." 表明嘀,"-" 表明嗒,點擊事件用 Down 表明 mousedown,Up 表明 mouseup。 this
200ms 間隔用來區別嘀嗒,1s 間隔用來區分一個 Morse 單元組的結束。 spa
// 點信號 = Down - Up = 間隔 < 200ms ?"." : "-";
// Down <200ms Up >1s = "." = E
// Down <200ms Up <1s Down >200ms Up >1s = ".", "-" = A
// 直接使用 fromEvent 操做符,來生成點擊操做的流,而後用 map 操做符轉化成時間戳,
// takeUntil 用來控制流的結束,避免重複訂閱。
const clickBegin$ = fromEvent(this.sendButtonElementRef.nativeElement, 'mousedown')
.pipe(
takeUntil(this.onDestroy$),
map(n => Date.now())
)
const clickEnd$ = fromEvent(this.sendButtonElementRef.nativeElement, 'mouseup')
.pipe(
takeUntil(this.onDestroy$),
map(n => Date.now())
) 複製代碼
第一步,識別點信號爲 「滴」 「嗒」
前面代碼已經拿到點擊事件的流,而且用 「map」 操做符,把數據轉化爲當前的時間戳。
下面開始計算 Down & Up 之間的間隔時間,思考,合併兩個流的的操做符有哪些呢?
// zip的實現
zip(clickBegin$, clickEnd$)
.pipe(
// 計算 Down - Up 間隔時間
map(this.toTimeDiff),
// 根據間隔時間,轉化爲嘀嗒替代字符 "." "-"
map(this.msToMorseCharacter)
)
.subscribe(result => {
// 發送到主信號流
morseSignal$.next(result);
}); 複製代碼
第二步,根據 「長間隔」 來切片分組
分組的操做符有哪些?
// 點擊持續狀態流
const clickKeeping$ = clickBegin$
.pipe(
// 替換爲新的流,不影響原來的流
switchMap(() => {
// 定時在持續發送數據,維持點擊中狀態
return timer(0, morseTimeRanges.lessThenlongBreak).pipe(
// 直到 Up 後結束點擊狀態
takeUntil(clickEnd$)
);
})
)
// 「長間隔」的切片流
const morseBreak$ = clickKeeping$.pipe(
debounceTime(morseTimeRanges.longBreak)
);
// 得到 Morse 單元組
morseSignal$
.pipe(
// 切片分組主信號流
buffer(morseBreak$) // 轉化爲,例如 ['.', '.', '.']
) 複製代碼
第三步,分組數據根據對應錶轉化出最終結果
join(’’) Morse 單元組去匹配對應表,很簡單不用說。
錯誤發生在 switchMap 中,分支流報錯,可是主流不會收到影響,而後用 catchError 捕捉錯誤。
// Morse 單元組去匹配對應表
private translateSymbolToLetter = morseArray => {
const morseCharacters = morseArray.join('');
const find = morseTranslations.find(n => n.symbol === morseCharacters)
// 這裏 find 可能爲 undefined 致使報錯,可是錯誤會被 catchError 捕捉
return find.char;
}
// 轉化+錯誤處理,最終完成
morseSignal$
.pipe(
buffer(morseBreak$),
switchMap(n => {
return of(n).pipe(
// 只爲了 Demo 演示中的展現用
tap(n => this.lastMorseGroupCharacters = n.join(' ')),
// 轉化成對應表中字符
map(this.translateSymbolToLetter),
// 捕捉錯誤
catchError(n => {
return of(morseCharacters.errorString);
})
)
})
)
.subscribe(result => {
// 輸出最終轉化結果
this.morseLog.push(result);
console.log('結果:', result)
}); 複製代碼
4、解讀 Michael Hladky 大神的示例
總體上,把 「嘀嗒」 「短間隔」 「長間隔」 都轉化成替代符,過濾無用的替代符,而後 filter 「長間隔」 替代符的流,來作 buffer 切片數據。其餘還有由於使用 combineLatest 操做符致使的不一樣。
// 識別 「嘀」 「嗒」
const morseCharFromEvents$ = observableCombineLatest(this.startEvents$, this.stopEvents$)
.pipe(
// 計算 mousedown mouseup 時間間隔
map(this.toTimeDiff),
// 轉化成標識符
map(this.msToMorseChar),
// 過濾 Morse 單元組中的 「短間隔「 標識符
filter(this.isCharNoShortBreak as any)
);
// 主信號流
this.morseChar$ = observableMerge(morseCharFromEvents$, this.injectMorseChar$)
// 識別 「長間隔「 標識符,來做爲切片流
const longBreaks$ = this.morseChar$
.pipe(filter(this.isCharLongBreak as any));
// 切片成 Morse 單元組
this.morseSymbol$ = this.morseChar$
.pipe(
buffer(longBreaks$),
map(this.charArrayToSymbol),
filter(n => (n !== '') as any)
)
// 錯誤處理 + 標識符對應錶轉化
this.morseLetter$ = this.morseSymbol$
.pipe(
switchMap(n => observableOf(n).pipe(this.saveTranslate('ERROR')))
);
// Up 後補4個 「長間隔「 標識符,用來作 Morse 單元組的結束
const breakEmitter$ = observableTimer(this.msLongBreak, this.msLongBreak)
.pipe(
mapTo(this.mC.longBreak),
take(4)
);
this.stopEventsSubject
.pipe(
switchMapTo(
breakEmitter$.pipe(takeUntil(this.startEventsSubject))
)
)
.subscribe(n => this.injectMorseChar(n)); 複製代碼
下圖是讀完《深刻淺出RxJS》後的學習筆記,標註了一些操做符的快速記憶特色,方便使用的適合查閱。
本文做者:甄帥
文章來源:Worktile技術博客
歡迎訪問交流更多關於技術及協做的問題。
文章轉載請註明出處。