RxJS:在特定事件之后发出缓冲区

时间:2017-08-09 13:09:31

标签: typescript rxjs

问题:我想创建Rxjs组合(函数链),这将导致从一个Observable缓冲值,直到某个事件发生,然后同步发送所有缓冲的值,然后缓冲直到下一个事件。

我使用此函数来收集所有必须等到我的应用程序拨打授权电话的http请求。然后运行所有这些请求。 (它在Angular4 HttpClient拦截器中实现),这就是我的用例,但我通常会寻求如何创建这样的rx链的解决方案。

为什么Rxjs缓冲区并不令人满意。从我读取和测试的缓冲区要么需要精确的时间帧,或者在获得调度程序而不是时间作为参数的情况下,它在检测到最后一个调度程序"事件"之后重新订阅调度程序。传播。我希望它的工作方式如下:当出现第一个请求时,我开始缓冲然后订阅调度程序,在调度程序发出后,停止缓冲,重新发送所有缓冲的值,并等待下一个新请求再次开始缓冲并且再次启动调度程序。

现在我的解决方案是使用未定义或我的可观察的辅助对象,代码大致如下:

private observable: Observable<boolean>;

makeRequest(): Observable<boolean> {
    if (this.observable !== void 0) {
        return this.observable;
    } else {
        this.observable = this.authenticationReuqest()
            .share()
            .finally(() => this.observable = void 0);

        return this.observable;
    }
}

通过这种方式,我可以缓冲我的请求,通过捕捉它们.delay()直到相同的多重观察发射,并且在它发出之后我只是清理它(尽管不需要取消订阅因为它最终清理完所以在完成之后或错误)。

如果有人有想法或模式如何用纯Rxjs替换这个解决方案,我很感兴趣。我有一些感觉,虽然我无法得到确切的解决方案,但Buffer和Zip的某些组合可以实现它。

由于 托马斯

3 个答案:

答案 0 :(得分:0)

你应该使用bufferWhen:它会从一个observable缓冲值,直到第二个值发出。

答案 1 :(得分:0)

我遇到了完全相同的问题,我有像这样的事件

enter image description here

我提前开始加载活动,以便我们不需要等待。但问题是,只有在一点之后(&#34; rdy&#34;,代表准备就绪)我想处理这些事件。因此,准备好的事件需要先行。我需要保持这些值直到准备就绪并重新发送它们。喜欢这个

enter image description here

以下是我的工作,我使用multicast在下游分享相同的订阅。在选择器函数中创建一个ready observable,过滤第一个ready事件。然后合并ready的observable和多播事件的缓冲区,取一个,并将switchMap从数组返回到值。最后结束多重事件。这是代码

const events = [
  'ev1',
  'ev2',
  'ready',
  'ev3',
  'ev4',
  'ev5'
]
Rx.Observable
  .interval(500)
  .take(events.length)
  .map(i => events[i])
  .multicast(
      () => new Rx.Subject(),
      events$ => {
          const ready$ = events$
            .filter(event => event === 'ready')
            .take(1)
          return Rx.Observable
            .merge(
                ready$,
                events$
                  .buffer(ready$)
                  .take(1)
                  .switchMap(events => Rx.Observable.from(events))
            )
                .concat(events$)
      }
  )

或者你可以找到它并在RxViz上运行https://rxviz.com/v/j8nNpe8N 它不漂亮,但它有效。我还在寻找一种更好的方法,任何想法?

答案 2 :(得分:0)

我也花了一些时间研究这个确切的问题,但我无法弄清楚。最后,我得到了以下工作。我相信必须有一种更加惯用/优雅的方法,但是作为RxJS的新用户,我能够理解这种方法。

import { fromEvent } from "rxjs";
import { tap, scan, filter, map } from "rxjs/operators";

const clicks = fromEvent(document, "click").pipe(
  scan(acc => acc + 1, 0),
  tap((v) => console.log("tapping", v))
);

const out = clicks.pipe(
  // tap(v => console.log('before scan', v)),
  scan((acc, count) => {
    if (count % 3 === 0) {
      return {acc: [], out: acc.acc.concat(count)}
    }
    return {out: [], acc: acc.acc.concat(count)}
  }, {acc: [] as number[], out: [] as number[]}),
  // tap(v => console.log('after scan', v)),
  filter(obj => obj.out.length > 0),
  map(obj => obj.out)
)

out.subscribe(v => console.log('out', v))

关键是我使用scan()作为运行中的减速器,然后滤除了值。我从.scan()中的每个后续值构建我自己的值“缓冲区”,然后在下一个过滤器中删除所有不具有“就绪”状态的事件。

看起来很丑,但是允许我的用例比准备好的事件复杂一些。我正在解析git-remote-helper命令,有些命令以双换行符结尾,有些则没有。