Skip to content

Instantly share code, notes, and snippets.

@vitaly-t
Last active November 4, 2024 10:26
Show Gist options
  • Save vitaly-t/6e3d285854d882b1618c7e435df164c4 to your computer and use it in GitHub Desktop.
Save vitaly-t/6e3d285854d882b1618c7e435df164c4 to your computer and use it in GitHub Desktop.
retry-async
/**
* Retry-status object type, for use with RetryCB.
*/
export type RetryStatus = {
/**
* Retry index, starting from 0.
*/
index: number,
/**
* Retry overall duration, in milliseconds.
*/
duration: number,
/**
* Last error, if available;
* it is undefined only when "retryAsync" calls "func" with index = 0.
*/
error?: any
};
/**
* Retry-status callback type.
*/
export type RetryCB<T> = (s: RetryStatus) => T;
/**
* Type for options passed into retryAsync function.
*/
export 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 milliseconds (no delay by default),
* or a callback that returns the delays.
*/
delay?: number | RetryCB<number>,
/**
* Error notifications.
*/
error?: RetryCB<void>
};
/**
* Retries async operation returned from "func" callback, according to "options".
*/
export function retryAsync<T>(func: RetryCB<Promise<T>>, options?: RetryOptions): Promise<T> {
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());
if ((typeof retry === 'function' ? (retry(s()) ? 1 : 0) : retry--) <= 0) {
return Promise.reject(e);
}
const d = typeof delay === 'function' ? delay(s()) : delay;
index++;
return d >= 0 ? (new Promise(a => setTimeout(a, d))).then(c) : c();
});
return c();
}
@vitaly-t
Copy link
Author

vitaly-t commented Aug 15, 2024

In case anyone else comes across this and also wants to print details of each retry, the return line of the retryAsycnc function (line 59) can be changed to the following:

Why would you want to change it that way when you already have error handler that provides the details?

Note that this works fine even when the wrapper function is not async.

No, it doesn't, retryAsync requires that the wrapper always returns a promise.

Also note that if the original function does not throw any exception but returns null or blank string or some specific

That is not applicable here! The original function is always async, this is what it is all about, retrying an async function, so it always returns a Promise. No need throwing an exception, though you can, if you want.

In all, I sense a lot of confusion around how retryAsync works, even though the source code is all up in front. Somehow you managed to make all the wrong assumptions here :) Perhaps you haven't worked with promises and async code in JavaScript enough.

@infinity-matrix if you have questions, open a discission in the repo, it will be easier there, as gists are too limited.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment