Home > Blockchain >  Angular RxJs How to await finalize observable?
Angular RxJs How to await finalize observable?

Time:09-17

The code below intercepts any http request in my ionic app and adds a loading animation along with retry logic. The issue is that sometimes if the request is fast, the finalize() callback is getting called before the loading animation has been created thus never removing the loading animation.

I believe the solution is to use async and await on the creation of the loading animation so the finalize() callback is not called until the animation has been created but I've failed to make it work.

export class HttpRequestInterceptor implements HttpInterceptor {

    loading: any;
    loaderToShow: any;

    constructor(
        private loadingCtrl: LoadingController,
        private alertCtrl: AlertController,
        private authService: AuthService) {

    }

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

    this.loadingCtrl.getTop().then(hasLoading => {
        if (!hasLoading) {
            this.loading = this.loadingCtrl.create({
                spinner: 'circular',
                translucent: true
            }).then(loading => loading.present());
        }
    })
    
    return next.handle(request).pipe(
        catchError(err => {
            if (err instanceof HttpErrorResponse) {
                console.log('error');
                console.log(err);
                this.loadingCtrl.getTop().then(hasLoading => {
                    if (hasLoading) {
                        this.loadingCtrl.dismiss();
                    }
                });
                switch ((<HttpErrorResponse>err).status) {
                    //Login
                    case 401:
                        this.PresentFailedAlert(err.error['message']);
                        return EMPTY;
                    case 409:
                        this.PresentFailedAlert(err.error['message']);
                        return EMPTY;
                    default:
                        return throwError(err);
                }
            } else {
                return throwError(err);
            }
        }),
        retryWhen(err => {
            let retries = 1;
            return err.pipe(
                delay(3000),
                map(error => {
                    if (retries   === 3) {
                        throw error;
                    }
                    return error;
                })
            )
        }),
        //After our all retries failed. They might not have internet or server is down
        catchError(err => {
            console.log(err);
            this.PresentFailedAlert(err.error['message']);
            return EMPTY;
        }),
        //After call is done
        finalize(() => {
            delay(500);
            this.loadingCtrl.getTop().then(hasLoading => {
                if (hasLoading) {
                    this.loadingCtrl.dismiss();
                }
            })
        })
    );

    
}

CodePudding user response:

Can you try use async function in finalize block

finalize(async() => {
            delay(500);
            await this.loadingCtrl.getTop().then(hasLoading => {
                if (hasLoading) {
                    this.loadingCtrl.dismiss();
                }
            })
        })

CodePudding user response:

Since the last catchError returns Empty observable not an error, you can add a higher-order mapping operator like switchMap or switchMapTo after it to get the result of the promise, and do what you want without finalize.

You can try the following:

return next.handle(request).pipe(
  catchError((err) => {
    if (err instanceof HttpErrorResponse) {
      console.log('error');
      console.log(err);
      this.loadingCtrl.getTop().then((hasLoading) => {
        if (hasLoading) {
          this.loadingCtrl.dismiss();
        }
      });
      switch ((<HttpErrorResponse>err).status) {
        //Login
        case 401:
          this.PresentFailedAlert(err.error['message']);
          return EMPTY;
        case 409:
          this.PresentFailedAlert(err.error['message']);
          return EMPTY;
        default:
          return throwError(err);
      }
    } else {
      return throwError(err);
    }
  }),
  retryWhen((err) => {
    let retries = 1;
    return err.pipe(
      delay(3000),
      map((error) => {
        if (retries   === 3) {
          throw error;
        }
        return error;
      })
    );
  }),
  //After our all retries failed. They might not have internet or server is down
  catchError((err) => {
    console.log(err);
    this.PresentFailedAlert(err.error['message']);
    return EMPTY;
  }),
  //After call is done
  delay(500),
  // use `from` to convert promise to observable.
  switchMapTo(from(this.loadingCtrl.getTop())),
  // check hasLoading and return EMPTY observable after that.
  switchMap((hasLoading) => {
    if (hasLoading) {
      this.loadingCtrl.dismiss();
    }
    return EMPTY;
  })
  // //After call is done
  // finalize(() => {
  //   delay(500);
  //   this.loadingCtrl.getTop().then((hasLoading) => {
  //     if (hasLoading) {
  //       this.loadingCtrl.dismiss();
  //     }
  //   });
  // })
);
  • Related