If we ignore the generators here, below is generic retry routine in TypeScript that can be used anywhere:
// retry-status object:
// - "index": retry index, starting from 0
// - "duration": retry overall duration, in ms
// - "error": last error, if available
type RetryStatus = { index: number, duration: number, error?: any };
// retry-status callback;
type RetryCB<T> = (s: RetryStatus) => T;
type RetryOptions = {
// maximum number of retries (infinite by default),
// or a callback to indicate the need for another retry;
retry?: number | RetryCB<Boolean>,
// retry delays, in ms, or a callback that returns them;
delay?: number | RetryCB<number>,
// error notifications;
error?: RetryCB<void>
};
// retries async operation returned from "func" callback, according to options;
// note that "func()" will receive "error" = undefined when "index" = 0.
function retryAsync<T>(func: RetryCB<Promise<T>>, options?: RetryOptions) {
const start = Date.now();
let index = 0, e: any;
let {retry = Number.POSITIVE_INFINITY, delay = -1, error} = options ?? {};
const s = () => ({index, duration: Date.now() - start, error: e});
const c: () => Promise<T> = () => func(s()).catch(err => {
e = err;
typeof error === 'function' && error(s());
const r = typeof retry === 'function' ? retry(s()) : retry--;
const d = typeof delay === 'function' ? delay(s()) : delay;
index++;
const t = () => r ? c() : Promise.reject(e);
return d >= 0 ? (new Promise(a => setTimeout(a, d))).then(t) : t();
});
return c();
}
You just pass it a function that creates an asynchronous operation, plus some optional parameters, and that's it. It is way more generic and reusable than the solutions proposed so far.
The code above was taken from this gist.
asyncGenerator
retry the last iteration when thewhile(true)
loop activates? As far as I understand,next()
will always be called on it, and without a "rewind" or something the like, it will never retry the same iteration. – Czernowitz