JavaScript:失敗するたびに待ち時間の間隔を広げていくリトライ処理関数(エクスポネンシャルバックオフ)

JavaScript:失敗するたびに待ち時間の間隔を広げていくリトライ処理関数(エクスポネンシャルバックオフ)

April 22, 2022

関数(TypeScript) #

TypeScript で記載しています。

/** Sleeps for the specified time (milliseconds). */
function sleep(timeMs: number): Promise<void> {
  return new Promise((resolve) => {
    setTimeout(resolve, timeMs);
  });
}

/** Execute function, if it fails, retry after waiting. */
async function tryWithWaiting(
  callback: () => any,
  waitTimeMs: number,
  limitTimeMs: number
): Promise<any> {
  if (waitTimeMs > limitTimeMs) {
    throw new Error('Timeout.');
  }
  const res = callback();
  if (res) {
    return res;
  }
  await sleep(waitTimeMs);
  const extendedWaitTimeMs = waitTimeMs * 2;
  return tryWithWaiting(callback, extendedWaitTimeMs, limitTimeMs);
}

実行結果 #

/** Returns true with a 10% probability or false with a 90% probability. */
function possiblySuccess(): boolean {
  return Math.random() >= 0.9;
}

(async () => {
  try {
    await tryWithWaiting(possiblySuccess, 100, 10_000);
    console.log('SUCCESS (^^)');
  } catch {
    console.log('TIMEOUT (;-;)');
  }
})();

// -> "SUCCESS (^^)" or "TIMEOUT (;-;)"

補足 #

失敗するたびに待ち時間の間隔を広げながらリトライする処理ですが、このアルゴリズムは、エクスポネンシャルバックオフ(Exponential backoff)というらしいです。

  • Exponential: 指数関数的
  • Backoff: 遅延

なので、指数関数的に遅延しながらリトライする処理、ということですね。

さらに、並列処理の競合を考慮に入れる場合は、ジッターというランダムな待ち時間を使用するのが有効です。

詳細は以下の記事をご参考ください。

参考 #