angularrxjs

Angular rxjs - Call API recursively till status is true


I have 4 API calls first is to authenticate and than to upload a file and check file status. Once file status is return true than call API 4. Issue is with the fileStatus, I need to check file status till it is returned true and than only move, however, it just checks once and move to the next switchMap. Below is the code -

  this.homeService.auth().pipe(
  switchMap(authToken => {
    return this.homeService.uploadFile(this.masterFile);
  }),
  // Check file status
  switchMap(fileId => {
    return this.homeService.fileStatus(fileId).pipe(
      expand(status => {
        if (status === true || retries >= 50) {
          return of(status);
        } else {
          retries++;
          return of(status).pipe(delay(2000), switchMap(() => this.homeService.fileStatus(fileId)));
        }
      }),
      takeWhile(status => status === false && retries < 50, true),
      catchError(error => throwError('Failed to confirm upload status after 50 retries'))
    );
  }),
  switchMap((status: boolean) => {
    if (status === true) {
      return this.processFurther();
    }
  })
).subscribe(response => {
  console.lot('All done');
}, error => {
  console.log('Error processing ', error);
});

Solution

  • Your logic seems a bit complicated. Here's a function that will retry a set number of times with a delay that you can just plug into your stream.

    function pollUntilResult<T>(
        fn: () => Observable<T>,
        matchFn: (value: T) => boolean, 
        retries: number, 
        delay: number = 0
      ): Observable<T> {
    
      return timer(0, delay).pipe(
        concatMap((_, index) => fn().pipe(map(value => ({ index, value }))) ),
        takeWhile(({ index, value }) => index < retries && !matchFn(value), true),
        last(),
        map(({ value }) => value)
      );
    }
    

    This function does the following:

    Your observable stream should now look like this. (I don't know what was going on with the catchError/throwError business, but that made no sense to me, and since your last switchMap didn't return an observable for false results, I moved it in tehre).

    this.homeService.auth().pipe(
      switchMap(authToken => {
        return this.homeService.uploadFile(this.masterFile);
      }),
      // Check file status
      switchMap(fileId => 
        pollUntilResult(() => this.homeService.fileStatus(fileId), 50, 2000)
      ),
      switchMap((status: boolean) =>
        (status === true) ? this.processFurther() : throwError('Failed to confirm upload status after 50 retries')
      })
    )