Skip to content
Advertisement

How to retry an async function with a delay in javascript?

I am trying to fetch a record from a database. Due to race conditions it is possible and even likely that the record isn’t there when I first try to fetch it. How do I wrap this in a retry logic without going mad? I seem to be too stupid for it

  const booking = await strapi.query("api::booking.booking").findOne({
    where: {
      id: id,
    },
  });

This code should retry n times with a delay of t milliseconds. Thanks and much love.

What I’ve tried:

async function tryFetchBooking(
  id,
  max_retries = 3,
  current_try = 0,
  promise
) {
  promise = promise || new Promise();

  // try doing the important thing
  const booking = await strapi.query("api::booking.booking").findOne({
    where: {
      id: id,
    },
  });

  if (!booking) {
    if (current_try < max_retries) {
      console.log("No booking. Retrying");
      setTimeout(function () {
        tryFetchBooking(id, max_retries, current_try + 1, promise);
      }, 500);
    } else {
      console.log("No booking. Giving up.");
      promise.reject(new Error("no booking found in time"));
    }
    promise.catch(() => {
      throw new Error(`Failed retrying 3 times`);
    });
  } else {
    console.log("Found booking with retry");
    promise.resolve(booking);
  }
}

const booking = await tryFetchBooking(id);

The thrown error:

This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason:
TypeError: Promise resolver undefined is not a function

Answer

That promise.reject()/promise.resolve() approach is not going to work, you cannot resolve a promise from the outside. And you shouldn’t need to – just return/throw from your async function! The only place where you need to construct a new Promise is in a little helper function

function delay(t) {
  return new Promise(resolve => {
    setTimeout(resolve, t);
  });
}

Then you can write your function in a recursive manner:

async function tryFetchBooking(
  id,
  max_retries = 3,
  current_try = 0,
) {
  let booking = await strapi.query("api::booking.booking").findOne({
    where: {
      id: id,
    },
  });

  if (!booking) {
    if (current_try < max_retries) {
      console.log("No booking. Retrying");
      await delay(500);
//    ^^^^^^^^^^^^^^^^
      booking = await tryFetchBooking(id, max_retries, current_try + 1);
//              ^^^^^^^^^^^^^^^^^^^^^
      console.log("Found booking with retry");
    } else {
      console.log("No booking. Giving up.");
      throw new Error("no booking found in time");
      // or if you prefer the other error message:
      throw new Error(`Failed retrying 3 times`);
    }
  }
  return booking;
}

or even in an iterative manner:

async function tryFetchBooking(id, maxRetries = 3) {
  let currentTry = 0;
  while (true) {
    const booking = await strapi.query("api::booking.booking").findOne({
      where: {
        id: id,
      },
    });

    if (booking) {
      return booking;
    }
    if (currentTry < maxRetries) {
      await delay(500);
      currentTry++;
    } else {
      console.log("No booking. Giving up.");
      throw new Error("no booking found in time");
    }
  }
}
Advertisement