import debug from 'debug'; import { sleep } from './util.mjs'; const log = debug('youtube-maze:retry'); /** * An error that is expected to be temporary such that the initial action * may be retried. */ export class TemporaryError extends Error { constructor(message) { super(message); this.name = this.constructor.name; Error.captureStackTrace(this, this.constructor); } } /** * Make an async function retry-able. * * When the underlying function throws a TemporaryError, the initial * call will be repeated, under the limits set below. * * @param function func Async function to call. * @param number retries Allowed number of retries before failing. * @param number cooldown Time to wait before retrying (ms). * @return function New function that is retryable. */ export const retryable = (func, retries = 3, cooldown = 1000) => { return async (...args) => { while (true) { let remRetries = retries; let curCooldown = cooldown; try { const result = await func(...args); return result; } catch (err) { if (err instanceof TemporaryError && remRetries > 0) { log(`\ ${func.name}(${args}) failed with error "${err.message}" Retrying in ${curCooldown} ms (${remRetries} retries remaining)`); await sleep(curCooldown); remRetries -= 1; curCooldown *= 2; } else { throw err; } } } }; };