Please ignore variable names and formatting as I've just changed them.
I've been trying to implement RxJS error handling for an observable that takes an action (user click) and then sends the request object from our form and passes that to an HTTP call as you can see below.
Issue: If I send a request (from a form) on our webpage that doesn't exist in the backend, it fails and I get an error back (great!), BUT if I try to resubmit a correct request after my failed attempt, the application just hangs and loads and doesn't do anything.
serivce.ts
file
sendRequest$ = this.exampleRatingSubjectAction$.pipe(switchMap(exampleRequest => this.http.post<exampleresponse>(this.PCFUrl + '/example',exampleRequest).pipe(
catchError(this.handleError)
)),
tap(data => console.log(data)),
);
sendExampleRequestEconomy$ = this.exampleSubjectActionEconomy$.pipe(switchMap(exampleRequest=> this.http.post<exampleresponse>(this.PCFUrl + '/example',exampleRequest)
.pipe(
catchError(this.handleError)
)),
tap(data => console.log(data)),
);
private handleError(err: HttpErrorResponse): Observable<never> {
// in a real world app, we may send the server to some remote logging infrastructure
//instead of just logging it to the console
let errorMessage: string;
if (err.error instanceof ErrorEvent) {
// A client-side or network error occured. Handle it accordingly
errorMessage = `An error occured: ${err.error.message}`;
} else {
// The backend returned an unsuccessful response code.
// the resposne body may contain clues as to what went wrong,
errorMessage = `Backend returned code ${err.status}: ${err.message}`;
}
console.error(err);
return throwError(errorMessage);
}
component code:
exampleResponsePriority$ = this.dataService.sendExampleRequest$
.pipe(
map(data => this.exampleResponsePriority = data),
tap(data => {
this.showPageLoader = false;
this.calculatorService.checkMinOrDeficitRate(data);
}),
catchError( err => {
this.showPageLoader = false;
// await this.presentAlert(err);
this.errorMessageSubject.next(err);
console.log("priority")
return EMPTY;
}));
exampleResponseEconomy$ = this.dataService.sendExampleRequestEconomy$
.pipe(
map(data => this.exampleResponseEconomy = data),
catchError( err => {
this.showPageLoader = false;
// await this.presentAlert(err);
this.errorMessageSubject.next(err);
console.log("economy")
return EMPTY;
}));
My only guess is our action stream has already been updated with a request object on the first click, then when the response from the backend is a 500, it's maybe stuck on it and won't allow for a resubmit?
I've tried playing around with updating the action stream on a failed request but couldn't get it to work.
I feel like I'm just a few lines of code away!
Any help would be great.
It looks like, in your final catchError
(in the component code), you return the EMPTY
RxJS stream as the error response. According to the RxJS docs for catchError
:
This operator handles errors, but forwards along all other events to the resulting observable. If the source observable terminates with an error, it will map that error to a new observable, subscribe to it, and forward all of its events to the resulting observable.
link to documentation, emphasis added.
Your source observer has terminated with an error, so it won't run any of those earlier steps in the pipeline. The subscription going forward will be listening to EMPTY
(doc link), meaning that the subscription handler will never be called (nor will the subscription terminate).
You may want to try returning the source observable, which is passed to catchError
as the second argument of the handler. For example, your code might become:
exampleResponsePriority$ = this.dataService.sendExampleRequest$
.pipe(
map(data => this.exampleResponsePriority = data),
tap(data => {
this.showPageLoader = false;
this.calculatorService.checkMinOrDeficitRate(data);
}),
catchError((err, caught) => {
this.showPageLoader = false;
// await this.presentAlert(err);
this.errorMessageSubject.next(err);
console.log("priority")
return caught;
}));
Additionally, you may want to look into the docs for the retry
operator, which can be used instead of (or in conjunction with) catchError
to achieve similar results.