繁体   English   中英

在rxjs中队列http调用

[英]Queue http calls in rxjs

我正在开发一个会话服务,用于检查身份验证令牌是否已过期。 如果是,则调用刷新令牌。 在此请求期间,所有传入的请求都应排队,并在请求完成后发出。 之后,所有传入的请求都可以在没有队列的情况下通过,直到令牌再次到期。 我为此画了一张大理石图:

1. ---a---b---c--d-----e--
2. -t-------f------t------
3. ---a---b---------cd-e--

我命名为1.作为incoming$ Observable,2。是valve$ - 如果它是真的,请求可以通过,如果它是假的,它们应该排队。 当它变为真时,排队被解雇。

到目前为止我做了什么? 我认为这应该通过添加一个名为receiver$的中间Observable来完成,它根据valve$更改其值。 valve$为真时,它只返回一个简单的主题,如果它是假的,它会返回一个能够记录值的主题。

receiver$ = valve.pipe(
  map((value) => {
    if (value) {
      return new Subject();
    } else {
      return (new Subject()).pipe(
        shareReplay(),
      );
    }
  })
);

然后,在incoming$获得的每个新值应该被添加到recevier$的当前可观察量:

incoming$.pipe(
  combineLatest(receiver$),
).subscribe((incomingValue, recevier) => {
  recevier.next(incomingValue);
});

这是我无法绕过的部分。 无论何时阀门变为真,我都需要receiver$的最后两个值。 倒数第二个将保留队列,最后一个将保持活动主题。 通过合并它们我可以实现我的目标。 我不知道如何实现这一点以及如何管理订阅。 而且,对于这种看似简单的用例来说,这看起来过于复杂。

实现此行为的最佳方法是什么?

你可以考虑这些方面的解决方案。

首先,您创建一个主题,通过该主题发出您想要发出的所有请求

const requests$ = new Subject<Observable<any>>()

然后创建一个主题,通过它来传达阀门的状态,即您是否可以立即执行请求,或者您必须缓冲它

const valve$ = new Subject<boolean>();

现在,您可以创建一个仅在阀门打开时传递请求的流,即如果valve$发出的最后一个值为true

const openStream$ = valve$.pipe(
  switchMap(valve => {
    if (valve) {
      return requests$;
    } else {
      return empty();
    }
  })
);

您还可以创建一个流,在关闭时缓冲所有请求

const bufferedStream$ = requests$.pipe(
  bufferToggle(valve$.pipe(filter(valve => !valve)), () => valve$.pipe(filter(valve => valve))),
  mergeMap(bufferedCalls => bufferedCalls)
)

现在你所要做的就是merge openStream$bufferedStream$subscribe生成的流,就像这样

merge(openStream$, bufferedStream$).pipe(
  mergeMap(request => request)
)
.subscribe(httpCallResult => {// do stuff})

我已经使用以下数据测试了此解决方案,使用Observables of string模拟真实的http调用

const requests$ = new Subject<Observable<string>>();
setTimeout(() => {requests$.next(of('A'))}, 50);
setTimeout(() => {requests$.next(of('B'))}, 60);
setTimeout(() => {requests$.next(of('C'))}, 100);
setTimeout(() => {requests$.next(of('D'))}, 110);
setTimeout(() => {requests$.next(of('E'))}, 130);
setTimeout(() => {requests$.next(of('F'))}, 250);
setTimeout(() => {requests$.next(of('G'))}, 260);
setTimeout(() => {requests$.next(of('H'))}, 300);
setTimeout(() => {requests$.next(of('I'))}, 310);
setTimeout(() => {requests$.next(of('L'))}, 330);


const valve$ = new Subject<boolean>();
setTimeout(() => {valve$.next(true)}, 30);
setTimeout(() => {valve$.next(false)}, 80);
setTimeout(() => {valve$.next(true)}, 120);
setTimeout(() => {valve$.next(false)}, 200);
setTimeout(() => {valve$.next(true)}, 290);

你可以通过使用concatMap来实现这一点, concatMap根据值valve$合并两个不同的流。 请注意,这要求valve$incoming$都与share()共享。

valve$
  .pipe(
    concatMap(v => v
      ? incoming$.pipe(takeUntil(valve$))
      : incoming$
        .pipe(
          takeUntil(valve$),
          bufferCount(Number.POSITIVE_INFINITY),
          mergeAll(),
        )
    ),
  )
  .subscribe(console.log)

现场演示: https//stackblitz.com/edit/rxjs6-demo-d3bsxb?file = index.ts

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM