Javascript 计数、间隔和事件的反应式扩展缓冲区

Javascript 计数、间隔和事件的反应式扩展缓冲区,javascript,rxjs,rxjs5,Javascript,Rxjs,Rxjs5,我想缓冲发送到服务器的事件。刷新缓冲区的触发器是缓冲区大小已达到、缓冲期已达到或窗口已卸载 我通过创建主题并使用带有关闭通知的buffer缓冲发送到服务器的事件。我对关闭通知程序使用race,并与window.beforeunload事件竞争缓冲期 this.event$ = new Subject(); this.bufferedEvent$ = this.event$ .buffer( Observable.race( Observable.i

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

我通过创建主题并使用带有关闭通知的
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个项目时,我不希望刷新它。

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

唯一有点麻烦的是fullBufferTrigger中的
.skip(1)
,但它是必需的,因为它会在缓冲区已满时触发(natch),但是
bufferedEvent$
中的缓冲区在触发之前似乎没有最新的事件

幸运的是,有了
timeoutTrigger
,最后一个事件就发出了。如果没有超时,fullBufferTrigger本身将不会发出最终事件

另外,将
buffer
更改为
bufferWhen
,因为前者似乎没有使用两个触发器触发,尽管您希望从文档中看到它。
footnote使用
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))
工作示例:

编辑:触发缓冲区的替代方法 由于
bufferWhen
race
的组合可能有点低效(每次事件发射时都会重新启动竞赛),因此另一种方法是将触发器合并到一个流中,并使用简单的
缓冲区

const bufferTrigger$ = timeoutTrigger
  .merge(fullBufferTrigger)
  .merge(beforeunloadTrigger)

const bufferedEvent$ = event$
  .buffer(bufferTrigger$)
  .filter(events => events.length > 0)

关于使用独立触发器的解决方案,有一件事让我感到困扰,
fullBufferTrigger
不知道
timeoutTrigger
何时发出了它的一个缓冲值,因此给定正确的事件序列,fullBuffer将在超时后提前触发

理想情况下,您希望在触发
timeoutTrigger
时重置
fullBufferTrigger
,但这很难做到

使用
bufferTime()
在RxJS v4中,有一个操作符
bufferWithTimeOrCount(timeSpan,count,[scheduler])
,它在RxJS v5中被卷积成一个额外的
bufferTime()
(从清晰的角度来看,这可能是一个错误)


工作示例:

我明白你的方向了。我尝试了类似的方法。你需要先数一数。类似于
event$.count().filter(c=>c>2)
。但问题是,由于没有订阅,Observable不会生成任何内容。因此,您正在使用
event$.next(event)
?那么event$.bufferCount(10)呢?这需要一个测试。是的,我正在使用event$.next(event)。我尝试使用bufferCount。。。这是一种工作方式,问题是当任何其他事件发生时,它不会重置为0。我认为在这种情况下,使用
race
从根本上说是有缺陷的,因为它选择了第一个发射并坚持它的可见光。bufferWhen通过在每个事件中调用函数来克服问题,因此在每个事件中重复竞争(是否效率低下?)。我实际上是在检查buffer(fullBufferTrigger.merge(timeoutTrigger.merge(beforeunloadTrigger)),这正是您的目标。谢谢!他们的医生真的不清楚!真是浪费时间
bufferTime<T>(
  bufferTimeSpan: number, 
  bufferCreationInterval: number, 
  maxBufferSize: number, 
  scheduler?: IScheduler
): OperatorFunction<T, T[]>;
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)