Daniiiii
Daniiiii

Reputation: 69

How to use the retry from Rxjs to my await function?

I have to return an await function from my code block, now when the await function is called and failed on the first try, I want to retry it again and if it failed on the second time.. I will show an error message.

THIS IS MY CODE

async makeCall(inputs: myInputs): Promise<Instance> {

    const emailOptions: CreateOptions = {
      to: inputs.toPhone,
      from: this.config.accountPhoneNumber
    };

    if (inputs.toPhone) {
      emailOptions.sendText = inputs.toPhone;
    }

    return await this.sample.create(emailOptions);
}

I WANT SOMETHING LIKE THIS OR ANY SUGGESTION? LIKE Retry from RxJs

 for(var i = 0; i < 1; i++)
      {
        var result = await this.sample.create(emailOptions);
        if(result)
        {
          // break the loop
          return result;
        }
      }
       // if we land here, so the number of retries was exceeded
       throw Error("...");

Upvotes: 4

Views: 98

Answers (3)

vitaly-t
vitaly-t

Reputation: 25940

Sometimes, a generic approach, without RxJs is simpler and better ;)

Here's generic (flexible and reusable) approach to retrying an async operation:

// retry-status object:
//  - "index": retry index, starting from 0
//  - "duration": retry overall duration, in ms
//  - "error": last error, if available
type RetryStatus = { index: number, duration: number, error?: any };

// retry-status callback;
type RetryCB<T> = (s: RetryStatus) => T;

type RetryOptions = {
    // maximum number of retries (infinite by default),
    // or a callback to indicate the need for another retry;
    retry?: number | RetryCB<Boolean>,

    // retry delays, in ms, or a callback that returns them;
    delay?: number | RetryCB<number>,

    // error notifications;
    error?: RetryCB<void>
};

// retries async operation returned from "func" callback, according to options;
// note that "func()" will receive "error" = undefined when "index" = 0.
function retryAsync<T>(func: RetryCB<Promise<T>>, options?: RetryOptions) {
    const start = Date.now();
    let index = 0, e: any;
    let {retry = Number.POSITIVE_INFINITY, delay = -1, error} = options ?? {};
    const s = () => ({index, duration: Date.now() - start, error: e});
    const c: () => Promise<T> = () => func(s()).catch(err => {
        e = err;
        typeof error === 'function' && error(s());
        const r = typeof retry === 'function' ? retry(s()) : retry--;
        const d = typeof delay === 'function' ? delay(s()) : delay;
        index++;
        const t = () => r ? c() : Promise.reject(e);
        return d >= 0 ? (new Promise(a => setTimeout(a, d))).then(t) : t();
    });
    return c();
}

You can still use it with RxJs, of course, because it is 100% generic. You just pass it a function that creates your async request, plus some optional parameters.

The code above was taken from this gist.

Upvotes: 1

Samuel Mazahery
Samuel Mazahery

Reputation: 352

if you call an api, best practice is to use an interceptor that retry your request in the following you can see:

@Injectable()
export class RetryInterceptorService implements HttpInterceptor {

  private maxRetryAttempts = 3; // Number of retry attempts

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(req).pipe(
      retryWhen(errors =>
        errors.pipe(
          mergeMap((error, index) => {
            if (index < this.maxRetryAttempts && this.shouldRetry(error)) {
              return of(error).pipe(delay(1000)); // Retry after 1 second
            }
            return throwError(error);
          })
        )
      ),
      catchError(this.handleError)
    );
  }

  private shouldRetry(error: HttpErrorResponse): boolean {
    // Define conditions under which to retry
    return error.status >= 500; // Retry for server errors
  }

  private handleError(error: HttpErrorResponse) {
    // Handle the error here
    console.error('An error occurred:', error);
    return throwError(() => new Error(error.message || 'Server Error'));
  }
}

if you want to use retry for another place with rxjs just use pipe and retryWhen

Upvotes: 0

Naren Murali
Naren Murali

Reputation: 58199

You can use this article code which seems to be exactly what you need. It has support for number of retries and custom error message.

import './style.css';
let i = 0;
const promiseFn = () => {
  const condition = i === 0;
  i++;
  return condition ? Promise.reject() : Promise.resolve();
};

const retryWithDelay = async (
  fn: any,
  retries = 3,
  finalErr = 'Retry failed'
) => {
  try {
    // try
    await fn();
  } catch (err) {
    // if no retries left
    // throw error
    if (retries <= 0) {
      console.log('error');
      return Promise.reject(finalErr);
    }
    //recursively call the same func
    return retryWithDelay(fn, retries - 1, finalErr);
  }
};

retryWithDelay(promiseFn, 2)
  .then(() => {
    console.log('final success');
  })
  .catch(() => {
    console.log('final error');
  });

Stackblitz Demo

Upvotes: 3

Related Questions