How can I set a delay in retryWhen
?
import 'rxjs/add/operator/retry';
import 'rxjs/add/operator/retrywhen';
...
constructor(http: Http) {
var headers = new Headers();
headers.append('Content-Type', 'text/plain');
http.post('https://mywebsite.azurewebsites.net/account/getip', "", { headers: headers })
.retryWhen(errors => {
return errors.delay(1000); // errors is not a function
})
(event) => {
// handle events
this.ip = event.json();
},
(error) => {
console.error(error);
toastr.error('No connection to server. Please reload the page!')
}
);
}
I am getting the error: errors is not a function
.
import {Http, Headers, Response} from '@angular/http';
http.get('http://jsonplaceholder.typicode.com/posts/1/commentsss')
.retryWhen(e => e.scan<number>((errorCount, err) => {
if (errorCount >= 10) {
throw err;
}
return errorCount + 1;
}, 0).delay(1000))
.subscribe(r => console.log(`Sub: ${r}`))
This retries for 10 times with 1 second delay.
plnkr
A little late, but this is how to do with a new version of rxjs ( >6) I guess you are trying to automatically retry network incase of a failure. This could be achieved via different ways, but here is a very small implementation RetryWhen() - THis operator catches if any errors are thrown by an observable and creates errorObservable from that. Now using the errorObservable we could retry for a specified number of attempts for a specific set of failure
For example, retries incase of a 404 failure, is really unnecessary, but incase of 500X exceptions it is really mandatory.
DelayWhen - we could use this operator to specify a timer observable that delays the next retry attempt until the time elapses. This also gives the additional advantage of lineraly increasing the delay between each retry attempts
iif - Use of this conditional operator really enables us to filter and execute the necessary observable based on a given condition. You could examples in stackoverflow as well. But I am going give a simple if else illustration
contactMap - This is higher-order observable operator, meaning it produces /maps an input/observable to an output observable. The reason for using this is, we need to redo the retry operation incase of the same failure for specified number of times and the best way to restart the operation is via an error Observable. Note that, we could use other higher-order observable operators like mergeMap/switchMap- each have their own advantages and disadvantages, but I prefer using this. Again contactMap is different from concat operator so care should be taken here
I find the best place to implement such a retry in Angular, is inside the httpinterceptors, but this could also be done inside the service
Here is a sample implementation:
// Class to intercept all network calls and retry for X number of times
export class ErrorInterceptor implements HttpInterceptor {
// private authService: UserAuthenticationService;
private ngxLogger: NGXLogger;
private errorHandlerService: ErrorHandlerService;
constructor(injector: Injector) {
this.ngxLogger = injector.get(NGXLogger);
this.errorHandlerService = injector.get(ErrorHandlerService);
}
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
const interceptObs$ = next.handle(req);
// you could filter for the URLS that this should be applied like this
if (req.url.match(API_FETCH_TOKEN)) {
let retryAttempts = 0;
const httpInterceptor$ = interceptObs$.pipe(
retryWhen(errorObs => {
return errorObs.pipe(
tap(errval => console.log(`Retrying because of err:${errval}`)),
concatMap(errObsValue => {
if (
errObsValue instanceof HttpErrorResponse &&
errObsValue.status != 404
) {
console.log('inside concatMap', errObsValue);
if (retryAttempts > APP_NET_RETRIES) {
return throwError(this.getErrorModel(errObsValue, req));
} else {
retryAttempts++;
// this linearly increases the delay of attempts
delayWhen(() =>
timer(retryAttempts * APP_NET_RETRIES_DELAY * 1000)
);
return httpInterceptor$;
}
}
})
);
})
);
return httpInterceptor$;
} else {
return interceptObs$;
}
// above is the notifier observable, with errors captured as input observable
// so we could operate on this observable for retires
// also make sure to return the error observable - i.e the notifier observable
// reason being all errors should again be returned so as to capture them and
// only when they are returned they can be retried
// Also make sure after take() - no.of retries we just throw a last occuring error obs
}
// I like to create a custom error model and handle that in Custom ErrorHandler
// Below is the sample implementation I use. but again this is your preference
// you can just rethrow the error alone
async getErrorModel(errValue, req): Promise<ErrorModel> {
const errModel = new ErrorModel();
errModel.Error = errValue;
errModel.ErrorMessage = 'Error in retrieving the token:' + errValue.message;
errModel.ErrorStatus = 421;
errModel.ErrorUrl = req.url;
errModel.Timestamp = momentTimeZone
.tz(DEFAULT_TIME_ZONE)
.format(DEFAULT_TIME_ZONE);
await this.errorHandlerService.handleError(errModel);
return Promise.resolve(errModel);
}
}
Hopefully, it helps..
EDIT: There is really a nice article about backoff-rxjs that really shortens eveything that we did above. Please refer this link
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With