简体   繁体   中英

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 like the delay to apply starting after the response from the last request. So like req->resp->1000ms->req etc.
  • I'd like to time-out each request. So for example if a request hasn't returned a response after 5 seconds, I'll cancel it and try again

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.

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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM