65 lines
1.7 KiB
JavaScript
65 lines
1.7 KiB
JavaScript
|
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;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
};
|