How does one poll a service with an effect that stops polling until the returned value of service meets a condition OR the total duration exceeds a timeout threshold?
For example: A backend system is generating a resource, a frontend application can check whether that resource is available by calling a REST api call which returns a boolean. In my NGRX application I would like to poll every 200 ms this api call until this api call returns the boolean true OR the total polling duration exeeds the threshold of 10000 ms.
The following code samples shows a polling mechanism, however, this polling cannot be cancelled nor has a timeout. How is this done?
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/observable/timer';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/switchMap';
@Effect()
pollEffect$: Observable<Action> = this.actions$
.ofType(tasksActions.ActionTypes.START_POLLING)
.switchMap(() => Observable.timer(0, 200)
.switchMap(() => this.myBackendService.getAvailability().map(response =>
return taskActions.ActionTypes.UpdateValue(response))
)
);
Had exactly same issue and I could not find answer from internet. It took me some time to make it work. Here is what I did. I noticed where I put 'takeUntil(this.pollingUntil$)' matters, I put it on top since I needed to update store with latest data. If I put it on bottom(commented line), then polling was canceled and I could not update store with latest data.
private readonly pollingIntervalMs = 5000;
private readonly maxPollingMs = 600000; // 10 sec
private pollingUntil$: Subject<boolean> = new Subject<boolean>();
@Effect()
pollDb$: Observable<Action> = this.actions$.pipe(
ofType(infraActions.InfraActionTypes.PollDb),
switchMap(pollAction => interval(this.pollingIntervalMs).pipe(
takeUntil(timer(this.maxPollingMs)),
takeUntil(this.pollingUntil$),
mapTo(pollAction),
switchMap(
(action: infraActions.PollDb) => this.dbService.get().pipe(
map((dbInfo: DbInfo) => {
if (meet condition) {
this.pollingUntil$.next(true);
}
return dbInfo;
})
)
),
// takeUntil(this.pollingUntil$),
map((dbInfo: DbInfo) => {
return new infraActions.PollDbSuccess(dbInfo);
}),
catchError(err => of(new infraActions.LoadDbFail(err)))
)),
);
You could try something like that:
@Effect()
pollEffect$: Observable<Action> = this.actions$
.ofType(tasksActions.ActionTypes.START_POLLING)
.switchMap(() => Observable.timer(0, 200)
// stop the polling after a 10s timeout
.takeUntil(Observable.of(true).delay(10000))
.switchMap(() => this.myBackendService.getAvailability()
.takeWhile(response => /* do your condition here */)
.map(response => taskActions.ActionTypes.UpdateValue(response))
)
);
This way, the polling will stop either:
- after a 10s timeout
OR
- if your condition based on the response is true
EDIT 1:
According to your comment, indeed I think you should rather do:
@Effect()
pollEffect$: Observable<Action> = this.actions$
.ofType(tasksActions.ActionTypes.START_POLLING)
.switchMap(() => Observable.timer(0, 200)
// stop the polling after a 10s timeout
.takeUntil(Observable.of(true).delay(10000))
.switchMap(() => this.myBackendService.getAvailability())
.takeWhile(response => /* do your condition here */)
.map(response => taskActions.ActionTypes.UpdateValue(response))
);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With