export async function delay(milliseconds: number): Promise { return new Promise((resolve) => { setTimeout(resolve, milliseconds); }); } export function exponentialBackoffDelay(currentFailureCount: number, minDelay: number, maxDelay: number, maxFailureCount: number) { let maxDelayRet = minDelay + ((maxDelay - minDelay) / maxFailureCount) * Math.max(currentFailureCount, maxFailureCount); return Math.round(Math.random() * maxDelayRet); } export type BackoffFunc = (callback: () => Promise) => Promise; export function createBackoff( opts?: { onError?: (e: any, failuresCount: number) => void, minDelay?: number, maxDelay?: number, maxFailureCount?: number }): BackoffFunc { return async (callback: () => Promise): Promise => { let currentFailureCount = 0; const minDelay = opts && opts.minDelay !== undefined ? opts.minDelay : 250; const maxDelay = opts && opts.maxDelay !== undefined ? opts.maxDelay : 1000; const maxFailureCount = opts && opts.maxFailureCount !== undefined ? opts.maxFailureCount : 50; while (true) { try { return await callback(); } catch (e) { if (currentFailureCount < maxFailureCount) { currentFailureCount++; } if (opts && opts.onError) { opts.onError(e, currentFailureCount); } let waitForRequest = exponentialBackoffDelay(currentFailureCount, minDelay, maxDelay, maxFailureCount); await delay(waitForRequest); } } }; } export const backoff = createBackoff({ onError(e, failuresCount) { console.warn(e); }, });