NodeJS 如何在javascript中重试一个有延迟的异步函数?

6vl6ewon  于 2023-03-17  发布在  Node.js
关注(0)|答案(3)|浏览(148)

我正在尝试从数据库中获取一条记录。由于争用条件,当我第一次尝试获取记录时,记录可能甚至很可能不存在。我如何将其 Package 在重试逻辑中而不出错?我似乎太笨了,做不到这一点

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

此代码应该重试n次,延迟为t毫秒。谢谢,非常喜欢。
我尝试过的:

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);

抛出的错误:

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
fslejnso

fslejnso1#

这种promise.reject()/promise.resolve()的方法是行不通的,你不能从外部解决一个承诺。你不应该需要--只需要从你的async函数中构造return/throw!你唯一需要构造new Promise的地方是在一个小的helper函数中

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

然后你可以用递归的方式来写你的函数:

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;
}

或者甚至以迭代的方式:

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");
    }
  }
}
nhn9ugyo

nhn9ugyo2#

import { strapiMock } from "./mock";

const wait = (ms) => new Promise((r) => setTimeout(r, ms));

// Credits to @Bergi
const retryOperation = (operation, delay, retries) =>
  operation().catch((reason) =>
    retries > 0
      ? wait(delay).then(() => retryOperation(operation, delay, retries - 1))
      : Promise.reject(reason)
  );

const throwIfNoResult = (result) => {
  if (!result) throw new Error("No result");
  return result;
};

const fetchBooking = (id) => {
  /*
  return strapi.query("api::booking.booking").findOne({
    where: {
      id: id
    }
  });
  */
  return strapiMock(id);
};

async function tryFetchBooking(id, delay = 1000, retries = 4) {
  const operation = () => fetchBooking(id).then(throwIfNoResult);
  const wrapped = retryOperation(operation, delay, retries);
  return await wrapped;
}

tryFetchBooking(1).then(console.log).catch(console.error);

使用的模拟:

let cnt = 0;

export const strapiMock = (id) => {
  return new Promise((resolve, reject) => {
    if (cnt++ === 3) {
      cnt = 0;
      // resolve(null);
      resolve(id);
    } else {
      reject("no data");
    }
  });
};

4nkexdtk

4nkexdtk3#

为了避免在循环中使用await,可以使用递归函数调用tryFetchBooking

async function tryFetchBooking(id, currentRetry = 0) {
  const maxRetries = 3;
  const booking = await strapi.query("api::booking.booking").findOne({
    where: {
      id: id,
    },
  });
  if (booking) {
    return booking;
  }
  if (currentRetry < maxRetries) {
    await delay(500);
    currentTry++;
    return tryFetchBooking(id, currentRety);
  } else {
    console.log("No booking. Giving up.");
    throw new Error("no booking found in time");
  }
}

相关问题