Retry with Exponential Back-Off utility method

Have you tried turning it on and off again? The web is a weird place and calls might not always succeed in the right manner. A retry with an exponential back-off mechanism helps your code to be more resilient when it connects to services outside of your control. While there are many packages that can help in this area, it pretty easy to add some utility methods to your project. In this article I'll show how you can create a general-purpose exponential back-off and retry mechanism using TypeScript and Node.js.

Wait for it...

Let's create a delay function first. It used the setTimeout mechanism:

export function delay(ms: number): Promise<void> {
  return new Promise<void>(resolve => {
    setTimeout(resolve, ms)

Retry and exponential back-off

The exponential back-off is important, because it prevents your code from hammering the other system if your call was unsuccessful. By using 2^x we get an exponential back-off sequence: 1, 2, 4, 8, 16.

export async function retryWithBackoff<T>(fn: () => Promise<T>, retries = 5, backoffInMs = 100): Promise<T> {
  let x = 0
  while (true) {
    try {
      return await fn()
    } catch (ex) {
      if (x == retries) {
        throw ex

      // exponential back-off
      let msToSleep = Math.pow(2, x) * backoffInMs

      // add some extra random ms to make sure we're not
      // retrying on exactly the same interval
      let rndSleep = Math.random() * 100

      await delay(msToSleep + rndSleep)
      x += 1


Let's test our code with the following example:

async function test() {
  try {
    await retryWithBackoff(() => {
      throw "auo"
  } catch (ex) {
    console.log("All is broken!")


This outputs:

mark: 0.495ms
mark: 128.712ms
mark: 268.073ms
mark: 478.778ms
mark: 834.063ms
mark: 1632.867ms
All is broken!
mark: 0.285ms

That's all you need.