分组操作符

buffer

buffer() 操作符的函数签名:

buffer([breakObservable])

buffer 本身意味着我们在等待而不会发出任何值,直到 breakObservable 发生。示例如下:

let breakWhen$ = Rx.Observable.timer(1000);

let stream$ = Rx.Observable.interval(200)
.buffer( breakWhen$ );

stream$.subscribe((data) => console.log( 'values',data ));

在这个案例中会一次性地发出值: values 0,1,2,3

业务场景

Auto complete(自动完成/智能提示) - 使用 buffer() 操作符进行处理的最显著的例子就是 auto complete 。但 auto complete 是如何工作的呢?我们来分步骤看下

  • 用户输入关键字
  • 搜索是基于这些按键的。重要的是,搜索本身是在打字时执行的,要么就执行多次,因为你输入了x个字符,要么采用更普遍的处理方式,就是打字完成后再执行搜索,还可以在输入时进行编辑。那么我们来执行这套解决方案的第一步:
let input = document.getElementById('example');
let input$  = Rx.Observable.fromEvent( input, 'keyup' )

let breakWhen$ = Rx.Observable.timer(1000);
let debounceBreak$ = input$.debounceTime( 2000 );

let stream$ = input$
.map( ev => ev.key )
.buffer( debounceBreak$ );

stream$.subscribe((data) => console.log( 'values',data ));

我们捕获 keyup 事件。我们还使用了 debounce() 操作符,本质上来说,一旦你停止打字x毫秒后它才会发出值。这个解决方案只是进行中的第一步,因为它只是报告了具体敲击的按键。一个更好的解决方案是捕获输入元素的实际内容,还可以执行 ajax 调用,所以让我们来看一个更精致的解决方案:

let input = document.getElementById('example');
let input$  = Rx.Observable.fromEvent( input, 'keyup' )

let breakWhen$ = Rx.Observable.timer(1000);
let debounceBreak$ = input$.debounceTime( 2000 );

let stream$ = input$
.map( ev => {
    return ev.key })
.buffer( debounceBreak$ )
.switchMap((allTypedKeys) => {
    // 执行 ajax
    console.log('Everything that happened during 2 sec', allTypedKeys)
    return Rx.Observable.of('ajax based on ' + input.value);
});

stream$.subscribe((data) => console.log( 'values',data ));

这个称之为“增强的 auto complete” 。原因如名字一样,我们保存了用户所做的每一个交互,然后才最终决定应该成为 Ajax 调用的最终输入。所以上面代码的结果应该看起来像下面这样:

// 出自 switchMap
Everything that happened during 2 sec ["a", "a", "a", "Backspace", "Backspace", "Backspace", "Backspace", "b", "b", "Backspace", "Backspace", "Backspace", "f", "g", "h", "f", "h", "g"]

// 出自 subscribe
values ajax based on fghfgh

如你所见,其实我们可以储存大量用户潜在的信息,而不只是做了 auto complete 搜索,我们可以储存他们是如何打字的,这或许很有趣,亦或是很无聊...

双击 - 在上面的示例中,我已经展示了按组捕获按键可以很有趣,而另一组可能有趣的 UI 事件就是鼠标点击事件,即单击、双击或三连击。如果不用 RxJS 的话,那写出的代码是极不优雅的,反之则轻而易举,像下面这样:

let btn = document.getElementById('btn2');
let btn$  = Rx.Observable.fromEvent( btn, 'click' )

let debounceMouseBreak$ = btn$.debounceTime( 300 );

let btnBuffered$ = btn$
.buffer( debounceMouseBreak$ )
.map( array => array.length )
.filter( count => count >= 2 )
;

btnBuffered$.subscribe((data) => console.log( 'values',data ));

多亏 debounce() 操作符,我们可以在发出任何值之前表达等待300毫秒。只是短短几行代码,就让我们很容易的可以决定 filter 应该怎么写。

bufferTime

bufferTime() 的函数签名:

bufferTime([ms])

作用是记录在该时间段内发生的所有事情并输出所有的值。下面的示例是以1秒为时间片段记录输入的所有活动事件。

let input = document.getElementById('example');
let input$  = Rx.Observable.fromEvent( input, 'input' )
.bufferTime(1000);

input$.subscribe((data) => console.log('all inputs in 1 sec', data));

在这个案例中你得到的输出会是这样的:

all inputs in 1 sec [ Event, Event... ]

得到的数据似乎没什么用,所以我们可能需要使用 map() 来使数据变得清晰,以便于看到用户实际的按键,像这样:

let input = document.getElementById('example');
let input$  = Rx.Observable.fromEvent( input, 'keyup' )
.map( ev => ev.key)
.bufferTime(1000);

input$.subscribe((data) => console.log('all inputs in 1 sec', data));

还要注意一点,我把事件改成了 keyup 。现在我们可以看见1秒内发生的所有 keyup 事件。

业务场景

如果你想要记录该网站上的其它用户正在做什么,并希望重播他们曾经做过的所有交互,或者当他们开始输入,你希望通过 socket 发送此信息的话,那么上面的示例会非常有用。最后一个是当下的标准功能,你看见一个人在另一个终端上打字。所以确实有这样的业务案例。

groupBy

TODO

results matching ""

    No results matching ""