Reactive Extensions缓冲计数,间隔和事件

时间:2017-10-15 20:37:07

标签: javascript rxjs rxjs5

我想缓冲发送到我服务器的事件。刷新缓冲区的触发器是已达到缓冲区大小,已达到缓冲区时间或已卸载窗口。

我通过创建主题并使用带有结束通知程序的buffer来缓冲发送到我的服务器的事件。我使用race作为结束通知程序,并使用window.beforeunload事件与缓冲期一起竞争。

this.event$ = new Subject();
this.bufferedEvent$ = this.event$
    .buffer(
        Observable.race(
            Observable.interval(bufferPeriodMs),
            Observable.fromEvent(window, 'beforeunload')
        )
    )
    .filter(events => events.length > 0)
    .switchMap(events =>
        ajax.post(
            this.baseUrl + RESOURCE_URL,
            {
                entries: events,
            },
            {
                'Content-Type': 'application/json',
            }
       )
    );

问题是,我现在如何限制缓冲区的大小。也就是说,当它有10个项目时,我从不希望刷新缓冲区。

2 个答案:

答案 0 :(得分:1)

这是我的工作解决方案。添加额外的console.log()以显示事件序列。

唯一令人讨厌的是fullBufferTrigger中的.skip(1),但它需要它,因为它会在缓冲区满(natch)时触发,但bufferedEvent$中的缓冲区似乎没有它被触发前的最新事件。

幸运的是,在timeoutTrigger到位的情况下,最后一个事件会被发出。没有超时,fullBufferTrigger本身不会发出最终事件。

此外,将buffer更改为bufferWhen,因为前者似乎没有触发两个触发器,尽管您希望它来自文档。
脚注buffer(race())比赛只完成一次,因此,此后首先使用的触发器将被使用,而另一个触发器将被忽略。相反,每次事件发生时bufferWhen(x => race())都会进行评估。

const bufferPeriodMs = 1000

const event$ = new Subject()
event$.subscribe(event => console.log('event$ emit', event))

// Define triggers here for testing individually
const beforeunloadTrigger = Observable.fromEvent(window, 'beforeunload')
const fullBufferTrigger = event$.skip(1).bufferCount(2)
const timeoutTrigger = Observable.interval(bufferPeriodMs).take(10)

const bufferedEvent$ = event$
  .bufferWhen( x => 
    Observable.race(
      fullBufferTrigger,
      timeoutTrigger
    )
  )
  .filter(events => events.length > 0)

// output
fullBufferTrigger.subscribe(x => console.log('fullBufferTrigger', x))
timeoutTrigger.subscribe(x => console.log('timeoutTrigger', x))
bufferedEvent$.subscribe(events => {
  console.log('subscription', events)
})

// Test sequence
const delayBy = n => (bufferPeriodMs * n) + 500 
event$.next('event1')
event$.next('event2')
event$.next('event3')

setTimeout( () => {
  event$.next('event4')
}, delayBy(1))

setTimeout( () => {
  event$.next('event5')
}, delayBy(2))

setTimeout( () => {
  event$.next('event6')
  event$.next('event7')
}, delayBy(3))

工作示例:CodePen

编辑:触发缓冲区的替代方法

由于bufferWhenrace的组合效率可能有点低(每次事件发生都会重新开始比赛),另一种方法是将触发器合并到一个流中并使用简单的{{1 }}

buffer

答案 1 :(得分:1)

使用 独立触发器 解决方案困扰我的一件事是fullBufferTrigger不知道timeoutTrigger何时发出其中一个缓冲值,因此给定正确的事件序列,fullBuffer将在超时后提前触发。

理想情况下,fullBufferTrigger会在timeoutTrigger触发时重置bufferTime(),但事实证明这很棘手。

使用bufferWithTimeOrCount(timeSpan, count, [scheduler])

在RxJS v4中有一个运算符bufferTime(),它在RxJS v5中被卷入另一个bufferTime<T>( bufferTimeSpan: number, bufferCreationInterval: number, maxBufferSize: number, scheduler?: IScheduler ): OperatorFunction<T, T[]>; 的签名(从清晰度的角度来看可能是一个错误)。

window.beforeunload

唯一剩下的问题是如何合并bufferTime触发器。查看onComplete的源代码,它应该在收到window.beforeunload时刷新它的缓冲区 因此,我们可以通过向缓冲的事件流发送onComplete来处理const bufferPeriodMs = 7000 // Set high for this test const bufferSize = 2 const event$ = new Rx.Subject() /* Create bufferedEvent$ */ const bufferedEvent$ = event$ .bufferTime(bufferPeriodMs, null, bufferSize) .filter(events => events.length > 0) const subscription = bufferedEvent$.subscribe(console.log) /* Simulate window destroy */ const destroy = setTimeout( () => { subscription.unsubscribe() }, 4500) /* Simulate Observable.fromEvent(window, 'beforeunload') */ const beforeunloadTrigger = new Rx.Subject() // Comment out the following line, observe that event7 does not emit beforeunloadTrigger.subscribe(x=> event$.complete()) setTimeout( () => { beforeunloadTrigger.next('unload') }, 4400) /* Test sequence Event stream: '(123)---(45)---6---7-----8--|' Destroy window: '-----------------------x' window.beforeunload: '---------------------y' Buffered output: '(12)---(34)---(56)---7' */ event$.next('event1') event$.next('event2') event$.next('event3') setTimeout( () => { event$.next('event4'); event$.next('event5') }, 1000) setTimeout( () => { event$.next('event6') }, 3000) setTimeout( () => { event$.next('event7') }, 4000) setTimeout( () => { event$.next('event8') }, 5000)

bufferTime的规范没有对onComplete进行明确的测试,但我想我已经设法将其放在一起了。

注意:

  • 将超时设置为大,将其从图片中取出进行测试。
  • 源事件流不受影响,说明事件8已添加但从不发出,因为窗口在它发生之前被销毁。
  • 要查看输出流 而不使用 beforeunloadTrigger,请注释掉发出onComplete的行。 Event7在缓冲区中,但不会发出。

测试:

beans {
    myCustomWidget1(Widget) {
        myClosure = { w -> return w.doThis() }
    }

    myCustomWidget2(Widget) {
        myClosure = { w -> return w.doThat() }
    }
}

工作示例:CodePen