Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save karol-majewski/9d2c37ee751484599d8b08b580616e08 to your computer and use it in GitHub Desktop.
Save karol-majewski/9d2c37ee751484599d8b08b580616e08 to your computer and use it in GitHub Desktop.
interface Options {
/**
* @default Infinity
*/
maximumAttempts?: number;
/**
* @default 0
*/
timeoutMs?: number
}
const defaults: Required<Options> = {
maximumAttempts: Infinity,
timeoutMs: 0,
}
const sleep = async (ms: number): Promise<void> => new Promise<void>((resolve) => setTimeout(resolve, ms));
const retry = ({ maximumAttempts, timeoutMs } = {
maximumAttempts: Infinity,
timeoutMs: 0,
}) => <T,>(thunk: () => Promise<T>): () => Promise<T> => {
let attempts = 0;
let startDate = Date.now();
const attempt = async (): Promise<T> => {
attempts++;
let result: T;
try {
result = await thunk();
} catch(error) {
const endDate = Date.now();
if (attempts === maximumAttempts || (timeoutMs > 0 && (endDate - timeoutMs) > startDate )) {
throw error;
}
result = await attempt()
}
return result;
}
return attempt;
}
const waitForEvent = retry({ maximumAttempts: 10, timeoutMs: 10000 })(() => {
return new Promise<boolean>(async (resolve, reject) => {
await sleep(1000);
if (Math.random() < 0.2) {
console.log('Done! We made it!')
resolve(true)
} else {
console.log('Nah, not ready yet.');
reject(false)
}
})
})
Promise.resolve().then(() => {
}).then(async () => {
console.time('Total time spent awaiting the state')
await waitForEvent();
console.timeEnd('Total time spent awaiting the state')
})
@karol-majewski
Copy link
Author

karol-majewski commented Jul 20, 2021

interface Options {
  /**
   * @default Infinity
   */
  maximumAttempts: number;
  /**
   * @default 0
   */
  timeoutMs: number;
}

export const retry = (options?: Partial<Options> = {}) => <T,>(thunk: () => Promise<T>): (() => Promise<T>) => {
  const { maximumAttempts = Infinity, timeoutMs = 0 } = options;
  let attempts = 0;
  const startDate = Date.now();
  const attempt = async (): Promise<T> => {
    attempts++;
    let result: T;
    try {
      result = await thunk();
    } catch (error) {
      const endDate = Date.now();
      if (attempts === maximumAttempts || (timeoutMs > 0 && endDate - timeoutMs > startDate)) {
        throw error;
      }
      result = await attempt();
    }
    return result;
  };
  return attempt;
};

/**
 * Asynchronous effect to retry.
 */
const fetchMyWebsite = () => fetch("https://website.rocks").then(response => response.body);

/**
 * Create a factory with provided configuration.
 */
const createRetriableRequest = retry({ maximumAttempts: Infinity, timeoutMs: 3600 * 1000 });

/**
 * Create a retriable effect with the same signature as the original function.
 */
const keepFetchingMyWebsite = createRetriableRequest(fetchMyWebsite)

/**
 * Usage
 */
async () => {
  await keepFetchingMyWebsite();

  console.log('Finally done!')
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment