At times, simplicity and effectiveness can be achieved without the need for RxJs ;)
Presented here is a versatile (flexible and reusable) method for retrying an asynchronous operation:
// Defining the retry-status object:
// - "index": current retry index, starting from 0
// - "duration": total duration of retries in milliseconds
// - "error": holds the last error encountered, if any
type RetryStatus = { index: number, duration: number, error?: any };
// Callback type for retry-status;
type RetryCB<T> = (s: RetryStatus) => T;
type RetryOptions = {
// Maximum number of retries (infinite by default),
// or a callback indicating the requirement for another retry;
retry?: number | RetryCB<Boolean>,
// Delay between retries in milliseconds,
// or a function returning delay time;
delay?: number | RetryCB<number>,
// Error notifications;
error?: RetryCB<void>
};
// Retries an asynchronous operation produced by the "func" callback based on options provided;
// Note that "func()" will receive "error" as 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 can utilize this approach with RxJs too, as it is completely generic. Simply provide a function that generates your asynchronous request along with any additional parameters.
The code snippet above has been sourced from this gist.