如何取消 delayWhen 间隔以避免重复间隔的 2 个并行线程

How to cancel delayWhen interval in order to avoid duplication of 2 parallel threads of intervals

在我加载主页时,我必须以 10 秒的间隔刷新 10 次。我已经用这段代码完成了:

refresh$ = createEffect(() => {
    return this.actions$.pipe(
       ofType(HomepageActions.RefreshSuccessAction),
       delayWhen(() => interval(10000)),
       switchMap(() => {console.log(this.retriesLeft, Date().toLocaleString())
        if (!this.retriesLeft) {
          return of(HomepageActions.RefreshIsOverAction());
        }

        this.retriesLeft--;

        return this.refreshService.getFreshData().pipe(
          map((data: PagingResponse<Data>) => {
            return HomepageActions.RefreshSuccessAction({payload: data});
          }),
          catchError((error: Error) => {
            return of(new GenericErrorAction(error));
          })
        );
      })
   );
});

我遇到的问题是在刷新过程中我导航到另一个页面然后 return 返回。它开始每 10 秒刷新 2 次。原因是因为我已经导航到另一个页面但是delayWhen还在等待一个时间间隔。

当我 return 回到主页时,等待的时间间隔开始执行,但与此同时我开始了一个新的刷新循环。这会导致两个并行循环设置间隔为 10 秒,从而导致大约 50 秒刷新 10 次,而不是每 10 秒刷新 10 次。

知道如何删除某些操作的 delayWhen 中的所有间隔,从而避免在返回时执行它们吗?

我认为您不需要在这种情况下使用 delayWhen()。您可以在 switchMap() 中声明一个 interval(10000) observable。这样,如果在 switchMap() 之前有任何发射,它将取消正在进行的间隔并重新开始。

这与 takeWhile() 相结合,您可以使用它来创建所需的结果。

refresh$ = createEffect(() => this.actions$.pipe(
  ofType(HomepageActions.RefreshSuccessAction),
  switchMapTo(interval(10000).pipe(
    tap(retryCount=>console.log(9-retryCount, Date().toLocaleString())),
    takeWhile(retryCount=>retryCount<10),
    switchMap(retryCount=>
      iif(
        ()=>retryCount<10, 
        getData$,
        of(HomepageActions.RefreshIsOverAction())
    ),
  ))
))

getData$ = this.refreshService.getFreshData().pipe(
  map((data: PagingResponse<Data>) => 
    HomepageActions.RefreshSuccessAction({payload: data})
  ),
  catchError((error: Error) => 
    of(new GenericErrorAction(error))
  )
);

这是逐行的

  • 因为我们不需要来自 actions$ 的值,我们使用 switchMapTo() 来订阅内部可观察对象。同样是switchMap(_=>interval(10000))
  • 我们声明了一个间隔,每次发射时都会从 0 开始计算。这取代了对外部计数器状态的需要,后者可能会导致副作用错误。
  • 使用 tap() 生成与原始示例相同的 console.log()
  • takeWhile() 将继续 observable 直到内部函数 returns true。我们的 observable 在这里停止,直到 actions$ 发出一个新值,再次重新启动整个过程。
  • 现在我们将 switchMap 从我们的计数器切换到处理第二个条件的内部可观察对象。
  • iif() 是 RxJS 的条件运算符。它根据条件函数订阅第一个或第二个可观察对象。

注意: 我声明了一个新的可观察变量 getData$ 以使 iif() 运算符更易于阅读。