前幾天我們講完了能把 Higher Order Observable 轉成一般的 Observable 的 operators,今天我們要講能夠把一般的 Observable 轉成 Higher Order Observable 的 operators。其實前端不太有機會用到這類型的 Operators,都是在比較特殊的需求下才會看到,但還是會有遇到的時候。


Operators

window

window 是一整個家族,總共有五個相關的 operators

  • window
  • windowCount
  • windowTime
  • windowToggle
  • windowWhen

這裡我們只介紹 window 跟 windowToggle 這兩個方法,其他三個的用法相對都簡單很多,大家如果有需要可以再自行到官網查看。
window 很類似 buffer 可以把一段時間內送出的元素拆出來,只是 buffer 是把元素拆分到陣列中變成

  1. Observable<T> => Observable<Array<T>>

而 window 則是會把元素拆分出來放到新的 observable 變成

  1. Observable<T> => Observable<Observable<T>>

buffer 是把拆分出來的元素放到陣列並送出陣列;window 是把拆分出來的元素放到 observable 並送出 observable,讓我們來看一個例子

  1. var click = Rx.Observable.fromEvent(document, 'click');
  2. var source = Rx.Observable.interval(1000);
  3. var example = source.window(click);
  4. example
  5. .switch()
  6. .subscribe(console.log);
  7. // 0
  8. // 1
  9. // 2
  10. // 3
  11. // 4
  12. // 5 ...

首先 window 要傳入一個 observable,每當這個 observable 送出元素時,就會把正在處理的 observable 所送出的元素放到新的 observable 中並送出,這裡看 Marble Diagram 會比較好解釋

  1. click : -----------c----------c------------c--
  2. source : ----0----1----2----3----4----5----6---..
  3. window(click)
  4. example: o----------o----------o------------o--
  5. \ \ \
  6. ---0----1-|--2----3--|-4----5----6|
  7. switch()
  8. : ----0----1----2----3----4----5----6---...

這裡可以看到 example 變成發送 observable 會在每次 click 事件發送出來後結束,並繼續下一個 observable,這裡我們用 switch 才把它攤平。
當然這個範例只是想單存的表達 window 的作用,沒什麼太大的意義,實務上 window 會搭配其他的 operators 使用,例如我們想計算一秒鐘內觸發了幾次 click 事件

  1. var click = Rx.Observable.fromEvent(document, 'click');
  2. var source = Rx.Observable.interval(1000);
  3. var example = click.window(source)
  4. example
  5. .map(innerObservable => innerObservable.count())
  6. .switch()
  7. .subscribe(console.log);

注意這裡我們把 source 跟 click 對調了,並用到了 observable 的一個方法 count(),可以用來取得 observable 總共送出了幾個元素,用 Marble Diagram 表示如下

  1. source : ---------0---------1---------2--...
  2. click : --cc---cc----c-c----------------...
  3. window(source)
  4. example: o--------o---------o---------o--..
  5. \ \ \ \
  6. -cc---cc|---c-c---|---------|--..
  7. count()
  8. : o--------o---------o---------o--
  9. \ \ \ \
  10. -------4|--------2|--------0|--..
  11. switch()
  12. : ---------4---------2---------0--...

從 Marble Diagram 中可以看出來,我們把部分元素放到新的 observable 中,就可以利用 Observable 的方法做更靈活的操作

windowToggle

windowToggle 不像 window 只能控制內部 observable 的結束,windowToggle 可以傳入兩個參數,第一個是開始的 observable,第二個是一個 callback 可以回傳一個結束的 observable,讓我們來看範例

  1. var source = Rx.Observable.interval(1000);
  2. var mouseDown = Rx.Observable.fromEvent(document, 'mousedown');
  3. var mouseUp = Rx.Observable.fromEvent(document, 'mouseup');
  4. var example = source
  5. .windowToggle(mouseDown, () => mouseUp)
  6. .switch();
  7. example.subscribe(console.log);

一樣用 Marble Diagram 會比較好解釋