19

In my angular app I'm seeing chrome (cancel) api calls that fire off too rapidly. I also have an HttpInterceptor that triggers a loading indicator with every HttpClient request after 500ms if the request has not completed. However, on the requests that get (cancelled) there does not seem to be any new event to subsequently hide my loading indicator.

Is there a way to detect 'cancelled' requests in the HttpInterceptor so I can hide my loading indicator again?

  export class GlobalHttpInterceptor implements HttpInterceptor {
    constructor(
      private sessionService: SessionService,
      private loadingService: LoadingService
    ) { }

    intercept(
      req: HttpRequest<any>,
      next: HttpHandler
    ): Observable<HttpEvent<any>> {

      setTimeout(() => {
        if (showLoading) {
          this.loadingService.show();
        }
      }, 500);
      let showLoading = true;

      if (this.sessionService.headers.length > 0) {
        for (const x of this.sessionService.headers) {
          req = req.clone({
            headers: req.headers.set(x.key, x.value)
          });
        }
      }

      return next.handle(req)
        .do(
          (response) => {
            if (response instanceof HttpResponse) {
              showLoading = false;
              this.loadingService.hide();
            }
          },
          (error) => {
            showLoading = false;
            this.loadingService.hide();
          }
        );
    }
  }
2
  • Have you figured out a way to do this yet? Commented May 14, 2018 at 21:14
  • 1
    Wrapping the handler response in another Observable, as outlined here, seems to work pretty well Commented May 14, 2018 at 21:48

3 Answers 3

27

Just encountered the same problem. The finalize operator seems to trigger even when the http request is cancelled.

    public intercept(
        request: HttpRequest<any>,
        next: HttpHandler
    ): Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> {
        // request starts

        return next.handle(request).pipe(
            finalize(() => {
                // request completes, errors, or is cancelled
            })
        );
    }
Sign up to request clarification or add additional context in comments.

1 Comment

But how do I differentiate between responses/requests? Logging them logs all the requests sent, with no difference between completed/failed/canceled requests.
16

Expanding on Davy's answer, this is how I'm detecting an aborted request.

As he stated, finalize will always run on the observable source completing, which in this case is either a success response, an error response, or an aborted request.

The key to my approach is tracking what you have received so far.

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
  let lastResponse: HttpEvent<any>;
  let error: HttpErrorResponse;

  return next.handle(request)
    .pipe(
      tap((response: HttpEvent<any>) => {
        lastResponse = response;
        if (response.type === HttpEventType.Response) {
          console.log('success response', response);
        }
      }),
      catchError((err: any) => {
        error = err;
        console.log('error response', err);
        // TODO: error handling if required
        return throwError(err);        
      }),    
      finalize(() => {
        if (lastResponse.type === HttpEventType.Sent && !error) {
          // last response type was 0, and we haven't received an error
          console.log('aborted request');
        }
      })
    );
}

1 Comment

Nice one! Used in a similar scenarion as OP (Http traffic status indicator which must return to idle especially dificult when requests get cancelled, on purpose/by design). 🙌🏼
0

Not sure why the answer provided by @Davy hasn't been marked with a green tick yet. It's a working solution that can be used without adhering to the abortable fetch.

Here is a working demo built with Angular 8.

Try it out with the network throttling (say, "Slow 3G") and notice the fancy animated circle that is being shown each and every time when there is at least one HTTP request in the application's requests queue (see more details in the source code).

If you look closely to the network requests you'll see that the previous pending XHR requests will have been canceled upon receiving a new input from a user (note that GitHub API imposes a limit for a number of HTTP GET requests so use wisely, otherwise you'll have 403 HTTP status code back). The approach proposed by @Davy works fine unless you do something fancy and press Cmd+S (Ctrl+S on PC) while there is a pending request. In that case, the finalize handler won't be invoked because the whole process was interrupted by a native modal dialog.

1 Comment

Accepting answers: people dont seem to do that anymore ;)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.