Helpers for automatic retrying of sketchy processes. Linear delay or exponential backoff. by @wellcaffeinated
Demo at: https://jsfiddle.net/gh/gist/library/pure/df1dc4bea62688c8f372d2b76b189d32
Helpers for automatic retrying of sketchy processes. Linear delay or exponential backoff. by @wellcaffeinated
Demo at: https://jsfiddle.net/gh/gist/library/pure/df1dc4bea62688c8f372d2b76b189d32
| <pre id="log"></pre> |
| // --- | |
| // Helpers for automatic retrying of sketchy processes. | |
| // Linear delay or exponential backoff | |
| // Made by @wellcaffeinated | |
| // No copyright | |
| // --- | |
| // Throw an instance of this in order to continue retrying | |
| class Retry extends Error {} | |
| // Use this to wrap a function that may fail. If the function | |
| // throws an instance of Retry, it will be attempted again | |
| // until it resolves or throws anything else | |
| const withRetry = (fn, { | |
| maxTries = false, | |
| dt = 1000, | |
| expBackoff = false, | |
| maxDelay = 60000, | |
| onRetry = null | |
| } = {}) => async (...args) => { | |
| let tries = 1 | |
| while (true) { | |
| try { | |
| return await fn.apply(this, args) | |
| } catch (error) { | |
| if (!(error instanceof Retry) || maxTries && tries >= maxTries) { | |
| throw error | |
| } | |
| const waitTime = expBackoff ? | |
| Math.min(maxDelay, Math.pow(2, tries - 1) * dt) : | |
| dt | |
| if (onRetry) { onRetry({ error, tries, waitTime }) } | |
| tries += 1 | |
| await new Promise(resolve => setTimeout(resolve, waitTime)) | |
| } | |
| } | |
| } | |
| // --- Usage examples: --- | |
| (async () => { | |
| const log = msg => document.getElementById('log').innerHTML += msg + '\n' | |
| const delay = (dt = 1000) => new Promise(resolve => setTimeout(resolve, dt)) | |
| const onRetry = ({ tries, waitTime }) => log(`Attempt number ${tries} failed, waiting: ${waitTime}ms`) | |
| let startTime | |
| const sketchyFunction = async (msg) => { | |
| await delay() | |
| if ((Date.now() - startTime) <= 8000){ | |
| throw new Error('Something went wrong') | |
| } else { | |
| log(msg) | |
| } | |
| } | |
| const linearDelay = withRetry( | |
| (msg) => sketchyFunction(msg).catch(e => Promise.reject(new Retry)), | |
| { onRetry } | |
| ) | |
| const expBackoff = withRetry( | |
| (msg) => sketchyFunction(msg).catch(e => Promise.reject(new Retry)), | |
| { onRetry, expBackoff: true } | |
| ) | |
| log('Linear delay') | |
| startTime = Date.now() | |
| await linearDelay("hello world") | |
| log('Exponential backoff') | |
| startTime = Date.now() | |
| await expBackoff("hello again") | |
| })() |
| name: Retry Utilities Example | |
| description: Helpers for automatic retrying of sketchy processes | |
| authors: | |
| - Jasper Palfree @wellcaffeinated | |
| normalize_css: no | |
| wrap: b | |
| panel_js: 2 |
| // --- | |
| // Helpers for automatic retrying of sketchy processes. | |
| // Linear delay or exponential backoff | |
| // Made by @wellcaffeinated | |
| // No copyright | |
| // --- | |
| // Throw an instance of this in order to continue retrying | |
| export class Retry extends Error {} | |
| // Use this to wrap a function that may fail. If the function | |
| // throws an instance of Retry, it will be attempted again | |
| // until it resolves or throws anything else | |
| export const withRetry = (fn, { | |
| maxTries = false, | |
| dt = 1000, | |
| expBackoff = false, | |
| maxDelay = 60000, | |
| onRetry = null | |
| } = {}) => async (...args) => { | |
| let tries = 1 | |
| while (true) { | |
| try { | |
| return await fn.apply(this, args) | |
| } catch (error) { | |
| if (!(error instanceof Retry) || maxTries && tries >= maxTries) { | |
| throw error | |
| } | |
| const waitTime = expBackoff ? | |
| Math.min(maxDelay, Math.pow(2, tries - 1) * dt) : | |
| dt | |
| if (onRetry) { onRetry({ error, tries, waitTime }) } | |
| tries += 1 | |
| await new Promise(resolve => setTimeout(resolve, waitTime)) | |
| } | |
| } | |
| } |