Dale
Dale

Reputation: 393

Angular/RxJS Polling with timeout and interval dependent on api response time

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

Answers (1)

Dale
Dale

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

Related Questions