如何取消 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()
运算符更易于阅读。
在我加载主页时,我必须以 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 直到内部函数 returnstrue
。我们的 observable 在这里停止,直到actions$
发出一个新值,再次重新启动整个过程。- 现在我们将 switchMap 从我们的计数器切换到处理第二个条件的内部可观察对象。
iif()
是 RxJS 的条件运算符。它根据条件函数订阅第一个或第二个可观察对象。
注意: 我声明了一个新的可观察变量 getData$
以使 iif()
运算符更易于阅读。