3

I'm trying to implement Angular5 http.get with retry. I wrote

http.get<any>('/api/study').retryWhen(
    (errors: Observable<any>):Observable<any> => {
        return errors.flatMap((error: any) => {
            if (error.status  == 500) {
                return Observable.of(error.status).delay(1000);
            }
            return Observable.throw(error);
        }).take(5);
    });

This will keep retrying up to 5 times if it gets error 500. However, if it gets 5 failures in a row, then it returns success (with null data). I don't want that. Instead I want it to throw the last error.

I tried to put .concat(Observable.throw({})) after the take(), and that works, but it does not give me any information, such as the status code of the most recent error.

How can I get the most recent error after the last retry fails?

John Henckel
  • 10,274
  • 3
  • 79
  • 79

2 Answers2

3

You don't need to use take(5) to count the failed attempts and count it yourself using a local variable.

For example you could do this:

http.get<any>('/api/study').retryWhen((errors: Observable<any>) => {
    let errors = 0;

    return errors.flatMap((error: any) => {
        if (errors++ < 5 && error.status  == 500) {
            return Observable.of(error.status).delay(1000);
        }
        return Observable.throw(error);
    });
});
martin
  • 93,354
  • 25
  • 191
  • 226
3

.retryWhen can only complete or error - in both cases, further retries are aborted. See docs.

So when you add .take(5) you are basically telling it to retry 5 times and then complete, at which point it can ONLY complete (and therefore cannot ALSO return an error - it is either one or the other, not both).

So using .take() will not solve your issue.

Unfortunately this means you have to keep track of the retry count yourself WITHIN the retryWhen - so for 5 counts you just let it return the error (which will trigger the retry), and finally on count 6 you manually throw an error. You can then catch that final error in a catch block.

Something like this (code not tested but shows the idea):

http.get<any>('/api/study').retryWhen(err => {
    console.log('retrying');
    let retries = 0;
    return err
      .delay(1000)
      .map(error => {
        if (retries++ === 6) {
          console.log('retry number ', retries);
          throw error; <- THIS WILL CAUSE retryWhen to complete
        }
        return error; 
      });
  })
  .catch(err => {
    console.log('caught');
    return Observable.of(err);
  })
  .subscribe(data => {
    console.log('subscriber');
    console.log(data);
  });

As a side note, adding .concat(Observable.throw({})) doesn't solve your problem, because when that fires, your 5 retries are done and hence the retryWhen has completed (ie. returned complete, so no error). This is why the concat cannot throw the last error, because it never received it.

rmcsharry
  • 5,363
  • 6
  • 65
  • 108