RxJS缓冲区和延迟直到另一个可观察到的第一次发出

时间:2020-03-25 17:32:26

标签: rxjs observable

制作复杂的可观察管道变得很困难,如果有人可以帮助我,我将不胜感激。

上下文

我有一个数据流,可以通过蓝牙给我一些值,这些值是我必须解码的数据帧。

这是一个名为RX$的BehaviorSubject。

现在RX$上,有时我会收到即时数据(INST),有时会收到历史数据(HIST)。使用INST,除了其他功能外,我还收到发送数据版本和模型的设备。我成功生成了一个可观察对象,该对象可以使用设备版本和模型为我计算一个JSON对象,并且只要两者都不存在就不会发出,我们称之为deviceVersionModelStream$

现在,另一方面,我以流的形式批量接收HIST数据帧,我们将其称为historyStream$,并且由于有大量数据,我使用bufferTime(2000)来构成数据数组并依靠我的嵌入式数据库批量插入(而不是一个接一个)。

到目前为止效果很好...

新用例

现在,我的客户添加了一条新规则,他们的设备类型很旧,无法为我提供特定案例的一些数据,但是使用相同的模式,我知道它还会给我带来什么。

因此,在解码帧并将其插入数据库之前,我需要具有设备版本和型号。

我的问题是,只要historyStream$发出一次(我在其他地方也使用过HOT),我该如何延迟deviceVersionModelStream$的发生?发生时,我想生成某种带有原始框架和版本/模型的JSON对象。

但是ALSO还是逐渐分发此信息,以免不像我的bufferTime(2000)那样淹没我的数据库批量插入内容。

我正在尝试使用缓冲区,mergeMap,延迟,但是我很难实现这一目标……

也许有RX实力的人可以帮助我吗?

非常感谢

2 个答案:

答案 0 :(得分:1)

这篇文章有点旧,但我最近在寻找“缓冲 rxjs observables 直到另一个事件发生”时偶然发现了它,我想我会分享更新版本的样子。

我是在 https://thinkrx.io 上构建的。这是我使用的代码:

const { rxObserver } = require('api/v0.3');
const { timer, of, combineLatest, concat } = require('rxjs');
const { delay, take, share, buffer, mergeAll, bufferTime, filter, takeUntil, skipUntil } = require('rxjs/operators');



const historyStream$ = timer(0, 10).pipe(
  share(), take(10)
);

const versionModel$ = of("A").pipe(
  delay(50), 
  take(1)
);

historyStream$.subscribe(rxObserver('History Stream'));
versionModel$.subscribe(rxObserver('Version Model'));

combineLatest([
  versionModel$,
  concat([
    historyStream$.pipe(buffer(versionModel$)),
    historyStream$.pipe(skipUntil(versionModel$))
  ]).pipe(
    mergeAll()
  )]
).subscribe(rxObserver("Buffer Window"));

输出如下:

Marble diagram of solution

这里发生的事情是我们将两个 observables 传递给 concat:一个代表缓冲的事件集,另一个代表事件进入时的流。我们最终将所有这些传递到mergeAll 以获得适当的效果。

答案 1 :(得分:0)

1总体思路-在缓冲和非缓冲之间来回切换

好像您需要在开始时暂停(缓冲)historyStream$,然后在deviceVersionModelStream$发出时取消暂停。暂停/取消暂停流的一种方法是。

merge(
  source$.pipe(bufferToggle(pauseOn$, () => pauseOff$)),
  source$.pipe(windowToggle(pauseOff$, () => pauseOn$))
).pipe(mergeAll())

另请参阅:https://medium.com/@kddsky/pauseable-observables-in-rxjs-58ce2b8c7dfd

您的具体情况

您的情况是:

const versionModel$ = deviceVersionModelStream$.pipe(take(1));
merge(
  historyStream$.pipe(bufferToggle(of(0), v => versionModel$)),
  historyStream$.pipe(windowToggle(versionModel$, v => NEVER))
).pipe(
  mergeAll(),
  bufferTime(2000)
);

如果希望每次发射都可以访问deviceVersionModelStream$的输出,则可以使用combineLatest。如果遇到对historyStream$deviceVersionModelStream$的多个订阅,则可以事先使用share

const versionModel$ = deviceVersionModelStream$.pipe(share(), take(1));
const historyStreamShared$ = historyStream$.pipe(share());
combineLatest(
  versionModel$,
  merge(
    historyStreamShared$.pipe(bufferToggle(of(0), v => versionModel$)),
    historyStreamShared$.pipe(windowToggle(versionModel$, v => NEVER))
  ).pipe(
    mergeAll(),
    bufferTime(2000)
  )
).subscribe(console.log);

https://stackblitz.com/edit/rxjs-oyctsg

编辑(如果您一开始只需要一次缓冲区)

在您的情况下,您无需在关闭缓冲区后就可以再次使用它,即,一旦从bufferToggle切换到windowToggle流,则无需切换回bufferToggle流。

这允许使用仅使用buffer而不是bufferTogglewindowToggle的稍微简单的方法。

2总体思路-仅在开头缓冲

source$ = dataStream.pipe(share()) // make sure this is a hot observable
pauseOff$ = timer(5000) // make sure this observable emits once and completes

concat(
  source$.pipe(buffer(pauseOff$), mergeAll()), // start with a buffered stream
  source$ // switch to the unbuffered stream when pauseOff$ emits and completes the previous stream
)

您的具体情况

const historyStream$ = timer(0, 100).pipe(share(), take(200));
const versionModel$ = of("version-model").pipe(delay(5000), take(1));

combineLatest(
  versionModel$,
  concat(
    historyStream$.pipe(buffer(versionModel$), mergeAll()),
    historyStream$
  ).pipe(
    bufferTime(2000)
  )
).subscribe(console.log);

https://stackblitz.com/edit/rxjs-2ptrux