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

前几天我们讲完了能把 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 是把元素拆分到阵列中变成

Observable<T> => Observable<Array<T>>

而 window 则是会把元素拆分出来放到新的 observable 变成

Observable<T> => Observable<Observable<T>>

buffer 是把拆分出来的元素放到阵列并送出阵列;window 是把拆分出来的元素放到 observable 并送出 observable,让我们来看一个例子

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 会比较好解释

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 才把它摊平。

当然这个范例只是想单存的表达 window 的作用,没什麽太大的意义,实务上 window 会搭配其他的 operators 使用,例如我们想计算一秒钟内触发了几次 click 事件

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 在前端比较少用到,但在后端或是比较複杂了前端应用才比较有机会用到。不知道读者有没有收穫呢? 如果有任何问题欢迎留言给我,谢谢。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容