Reputation: 393
I'm trying to write a method that will poll for the status of an API operation, returning an observable that will emit the result of each status call (since it contains progress information I would like to display) until it gets a completion status, emits that status, and then it's done.
What I have so far is this:
pollStatus(identifier: string): Observable<OperationStatusResource> {
const obs = interval(1000)
.pipe(
startWith(0),
switchMap( () => this.apiService.operationStatus(identifier) ),
takeWhile( (value, index) => {
return value.status != OPERATION_STATUS.COMPLETE;
}, true)
)
return obs;
}
which works, and I understand it, but I think I can do better and I'm struggling to figure out how. This requests a new status every second and cancels the previous one if it hasn't completed yet. I DO want to cancel previous requests any time I send a new one to ensure that I never emit out of order due to internet shenanigans, but:
I'd also be happy with any other way of accomplishing the same idea of being patient if the response takes a while, but not willing to wait forever, and not requesting too rapidly if the response comes back quickly either.
Bonus points if I can add an overall failure condition or timeout like give up if you get 3 timeouts in a row or if the whole thing takes more than 5 minutes or if I get 3 non-200 codes back in a row.
Upvotes: 2
Views: 2764
Reputation: 393
After sleeping on it, I think that THIS answer with an added retry at the end is what I should use.
pollStatus(identifier: string): Observable<OperationStatusResource> {
const obs = defer(() => this.apiService.operationStatus(identifier))
.pipe (
timeout(10000),
repeatWhen(notification => notification.pipe(delay(1000))),
takeWhile( (value, index) => {
return value.status != OPERATION_STATUS.COMPLETE;
}, true)
).pipe(retry(1))
return obs;
}
It will make the initial request, then wait for a response for a maximum number of ms (timeout) and after getting the response it will wait a number of ms (delay).
The retry at the end is what I needed. If the timeout is hit the request will be cancelled, and the retry will try again (once, in this example)
Upvotes: 2