Konstantin Kim
Konstantin Kim

Reputation: 239

How to keep observable chain going after http error in one observable

I've got a chain of observables. Here is the piece of code:

        //get dynamic tree 
      flatMap(res => this.dynamicReportId !== null ? this.reportService.getDynamicTree(this.dynamicReportId) : of({})),
      tap(res => this.dynamicTree = res),
        //get dynamic report 
      flatMap((res) => this.dynamicReportId !== null ? this.reportService.getDynamicReport(this.dynamicReportId) : of({})),

However when I got 500 API error from the first request(get dynamic tree), the chain stops and never enters the second flatMap(dynamic report).

Here is the function this.reportService.getDynamicTree():

 get(path: string, params: HttpParams = new HttpParams()): Observable<any> {
    return this.http.get(`${environment.apiBaseUrl}${path}`, {headers: this.setHeaders(true), params: params}).pipe(
      catchError(error => this.processError(path, error)),
      map((res: Response) => res));
  }

where this.processError returns observableThrowError(res);

What should I return in case of error to continue the chain? Or is the reason in other things?

Upvotes: 5

Views: 917

Answers (2)

Guerric P
Guerric P

Reputation: 31825

In case of an error, an Observable completes, so if you want to return a new Observable after an error occurs, you have to use the catchError operator like this:

//get dynamic tree 
flatMap(res => this.dynamicReportId !== null ? this.reportService.getDynamicTree(this.dynamicReportId) : of({})),
tap(res => this.dynamicTree = res),
catchError(() => of({})),
//get dynamic report 
flatMap((res) => this.dynamicReportId !== null ? this.reportService.getDynamicReport(this.dynamicReportId) : of({})),

The tap operator safely doesn't execute the callback and just forwards errors when they occur so that will assign this.dynamicTree only in case of a success.

If you want to handle the error in the service instead of the component, you have to return something else than observableThrowError(res); from processError (for example of({})) but that will execute the tap callback and assign this.dynamicTree which is probably an unwanted behavior.

Upvotes: 6

Jiri Kralovec
Jiri Kralovec

Reputation: 1617

First of - continuing the chain kind of defeats the whole concept of a chain. However, I think this issue should be resolveable by having some fairly simple chain iterator and handling errors thrown by HttpClient and service.

I created a StackBlitz example with my solution: https://stackblitz.com/edit/chain. For simplycity, the chain returns data in { result: any, error: any }[]. Of course you will be able to do pretty much anything in terms of piping operators to adjust the result.

Ideally, the observableChain functionality would be separate and handling the empty results as you need it to - in your case instead of returning { result: any, error: any }[] it would return {} on error response and log the error.

Upvotes: 2

Related Questions