freddi301
1/4/2018 - 12:01 PM

Generic retry function decorator (typescript)

Generic retry function decorator (typescript)

export function retrySync<Fun extends (...args: any[]) => any, State>({
  fun,
  predicate,
  nextState,
  state
}: {
  fun: Fun;
  state: State;
  nextState: (state: State) => State;
  predicate: (state: State) => boolean;
}): Fun {
  return function(...args) {
    try {
      return fun.apply(this, args);
    } catch (error) {
      if (predicate(state))
        return retrySync({
          fun,
          predicate,
          nextState,
          state: nextState(state)
        });
      else throw error;
    }
  } as any;
}

export function retryAsync<
  Fun extends (...args: any[]) => Promise<any>,
  State
>({
  fun,
  predicate,
  nextState,
  state
}: {
  fun: Fun;
  state: State;
  nextState: (state: State) => State;
  predicate: (state: State) => boolean;
}): Fun {
  return async function(...args) {
    try {
      return await fun.apply(this, args);
    } catch (error) {
      if (await predicate(state))
        return retrySync({
          fun,
          predicate,
          nextState,
          state: await nextState(state)
        });
      else throw error;
    }
  } as any;
}

const greaterThanZero = (n: number) => n > 0;
const decrease = (n: number) => n - 1;

export function retrySyncTimes<Fun extends (...args: any[]) => any>({
  times,
  fun
}: {
  times: number;
  fun: Fun;
}) {
  return retrySync({
    fun,
    predicate: greaterThanZero,
    nextState: decrease,
    state: times
  });
}

export function retryAsyncTimes<Fun extends (...args: any[]) => Promise<any>>({
  times,
  fun
}: {
  times: number;
  fun: Fun;
}) {
  return retryAsync({
    fun,
    predicate: greaterThanZero,
    nextState: decrease,
    state: times
  });
}

export const retry = {
  sync: {
    custom: retrySync,
    times: retrySyncTimes
  },
  async: {
    custom: retryAsync,
    times: retryAsyncTimes
  }
};