Retry Utilities Example

Helpers for automatic retrying of sketchy processes author(s): Jasper Palfree @wellcaffeinated

HTML

<pre id="log"></pre>

JavaScript 1.7

// ---
// 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")
  
})()