wolfhoundjesse
wolfhoundjesse

Reputation: 1133

another rxjs Observable retry strategy

I've seen a dozen ways to implement retry strategies, and I feel like I'm really close. What I have here doesn't throw an error after takeWhile() completes.

getItems(): Observable<ListItem[]> {
    return this.authHttp.get(URL + '/items')
      .retryWhen(e => 
        e.scan((errorCount, err) => errorCount + 1, 0)
          .takeWhile(errorCount => errorCount < 4)
          .delay(3000))
      .map(this.dataService.extractData)
      .catch(err =>  this.dataService.handleError(err, 'The items could not be retrieved.'))
  }

I've seen some jagged looking code snippets that might get it done. I experimented with inserting a .finally() before the .delay().

Once I get this one solved, I'll want to pass the errorCount back up to my subscription so that I can display something useful. "Retry 1 of 4." Something to let the user know what is going on.

Upvotes: 3

Views: 1941

Answers (1)

cartant
cartant

Reputation: 58430

With retryWhen there are several things you can do with the observable passed to the notifier. You can:

  • emit the received error notification to retry;
  • complete the observable to give up retrying without an error;
  • throw an error to give up retrying with an error.

In using takeWhile, you're emitting notifications and are then completing - which sees the retries cease with no error.

It sounds like you want to emit the notifications, but then throw an error:

getItems(): Observable<ListItem[]> {
  return this.authHttp.get(URL + '/items')
    .retryWhen(e => e.scan((errorCount, error) => {
        if (errorCount >= 4) {
          throw error;
        }
        return errorCount + 1;
      }, 0)
      .delay(3000)
    )
    .map(this.dataService.extractData)
    .catch(err =>  this.dataService.handleError(err, 'The items could not be retrieved.'));
}

To compartmentalize the retry logic into a reusable function, you can use the let - "let me have the whole observable" - operator:

import 'rxjs/add/operator/let';

function handleRetry<T>(source: Observable<T>): Observable<T> {
  return source.retryWhen(e => e.scan((errorCount, error) => {
      if (errorCount >= 4) {
        throw error;
      }
      return errorCount + 1;
    }, 0)
    .delay(3000)
  );
}

getItems(): Observable<ListItem[]> {
  return this.authHttp.get(URL + '/items')
    .let(handleRetry)
    .map(this.dataService.extractData)
    .catch(err =>  this.dataService.handleError(err, 'The items could not be retrieved.'));
}

Upvotes: 3

Related Questions