30 天精通 RxJS(20): Observable Operators - window, windowToggle, groupBy

前幾天咱們講完了能把 Higher Order Observable 轉成通常的 Observable 的 operators,今天咱們要講可以把通常的 Observable 轉成 Higher Order Observable 的 operators。javascript

其實前端不太有機會用到這類型的 Operators,都是在比較特殊的需求下才會看到,但仍是會有遇到的時候。html

Operators

window

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

  • window
  • windowCount
  • windowTime
  • windowToggle
  • windowWhen

這裏咱們只介紹 window 跟 windowToggle 這兩個方法,其餘三個的用法相對都簡單不少,你們若是有須要能夠再自行到官網查看。java

window 很相似 buffer 能夠把一段時間內送出的元素拆出來,只是 buffer 是把元素拆分到數組中變成後端

Observable<T> => Observable<Array<T>>複製代碼

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

Observable<T> => Observable<Observable<T>>複製代碼

buffer 是把拆分出來的元素放到數組並送出數組;window 是把拆分出來的元素放到 observable 並送出 observable,讓咱們來看一個例子bash

var click = Rx.Observable.fromEvent(document, 'click');
var source = Rx.Observable.interval(1000);
var example = source.window(click);

example
  .switch()
  .subscribe(console.log);
// 0
// 1
// 2
// 3
// 4
// 5 ...複製代碼

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

click  : -----------c----------c------------c--
source : ----0----1----2----3----4----5----6---..
                    window(click)
example: o----------o----------o------------o--
         \          \          \
          ---0----1-|--2----3--|-4----5----6|
                    switch()
       : ----0----1----2----3----4----5----6---...複製代碼

這裏能夠看到 example 變成發送 observable 會在每次 click 事件發送出來後結束,並繼續下一個 observable,這裏咱們用 switch 才把它攤平。spa

固然這個範例只是想單存的表達 window 的做用,沒什麼太大的意義,實務上 window 會搭配其餘的 operators 使用,例如咱們想計算一秒鐘內觸發了幾回 click 事件.net

var click = Rx.Observable.fromEvent(document, 'click');
var source = Rx.Observable.interval(1000);
var example = click.window(source)

example
  .map(innerObservable => innerObservable.count())
  .switch()
  .subscribe(console.log);複製代碼

JSBin | JSFiddle

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

source : ---------0---------1---------2--...
click  : --cc---cc----c-c----------------...
                    window(source)
example: o--------o---------o---------o--..
         \        \         \         \
          -cc---cc|---c-c---|---------|--..
                    count()
       : o--------o---------o---------o--
         \        \         \         \
          -------4|--------2|--------0|--..
                    switch()
       : ---------4---------2---------0--...複製代碼

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

windowToggle

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

var source = Rx.Observable.interval(1000);
var mouseDown = Rx.Observable.fromEvent(document, 'mousedown');
var mouseUp = Rx.Observable.fromEvent(document, 'mouseup');

var example = source
  .windowToggle(mouseDown, () => mouseUp)
  .switch();

example.subscribe(console.log);複製代碼

JSBin | JSFiddle

同樣用 Marble Diagram 會比較好解釋

source   : ----0----1----2----3----4----5--...

mouseDown: -------D------------------------...
mouseUp  : ---------------------------U----...

        windowToggle(mouseDown, () => mouseUp)

         : -------o-------------------------...
                  \
                   -1----2----3----4--|
                   switch()
example  : ---------1----2----3----4---------...複製代碼

從 Marble Diagram 能夠看得出來,咱們用 windowToggle 拆分出來內部的 observable 始於 mouseDown 終於 mouseUp。

groupBy

最後咱們來說一個開發上比較經常使用的 operators - groupBy,它能夠幫咱們把相同條件的元素拆分紅一個 Observable,其實就跟日常在 SQL 下是同樣個概念,咱們先來看個簡單的例子

var source = Rx.Observable.interval(300).take(5);

var example = source
              .groupBy(x => x % 2);

example.subscribe(console.log);

// GroupObservable { key: 0, ...}
// GroupObservable { key: 1, ...}複製代碼

JSBin | JSFiddle

上面的例子,咱們傳入了一個 callback function 並回傳 groupBy 的條件,就能區分每一個元素到不一樣的 Observable 中,用 Marble Diagram 表示以下

source : ---0---1---2---3---4|
             groupBy(x => x % 2)
example: ---o---o------------|
            \   \
            \   1-------3----|
            0-------2-------4|複製代碼

在實際上,咱們能夠拿 groupBy 作完元素的區分後,再對 inner Observable 操做,例以下面這個例子咱們將每一個人的分數做加總再送出

var people = [
    {name: 'Anna', score: 100, subject: 'English'},
    {name: 'Anna', score: 90, subject: 'Math'},
    {name: 'Anna', score: 96, subject: 'Chinese' }, 
    {name: 'Jerry', score: 80, subject: 'English'},
    {name: 'Jerry', score: 100, subject: 'Math'},
    {name: 'Jerry', score: 90, subject: 'Chinese' }, 
];
var source = Rx.Observable.from(people)
                           .zip(
                             Rx.Observable.interval(300), 
                             (x, y) => x);

var example = source
  .groupBy(person => person.name)
  .map(group => group.reduce((acc, curr) => ({ 
        name: curr.name,
        score: curr.score + acc.score 
    })))
    .mergeAll();

example.subscribe(console.log);
// { name: "Anna", score: 286 }
// { name: 'Jerry', score: 270 }複製代碼

JSBin | JSFiddle

這裏咱們範例是想把 Jerry 跟 Anna 的分數個別做加總,畫成 Marble Diagram 以下

source : --o--o--o--o--o--o|

  groupBy(person => person.name)

       : --i--------i------|
           \        \
           \         o--o--o|
            o--o--o--|

       map(group => group.reduce(...))

       : --i---------i------|
           \         \
           o|        o|

             mergeAll()
example: --o---------o------|複製代碼

今日小結

今天講了兩個能夠把元素拆分到新的 observable 的 operators,這兩個 operators 在前端比較少用到,但在後端或是比較複雜了前端應用才比較有機會用到。不知道讀者有沒有收穫呢? 若是有任何問題歡迎留言給我,謝謝。

相關文章
相關標籤/搜索