-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathretry.ts
82 lines (77 loc) · 2.3 KB
/
retry.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/**
* @file retry.ts
* @description Retry an async function for exponential backoff. With a retryFetch implementation.
* @copyright 2020-2024 Brandon Kalinowski (brandonkal)
* @license MIT
*/
import { delay as wait } from "jsr:@std/[email protected]";
/**
* Retry an async function until it does not throw an exception.
*
* @param fn the async function to execute
* @param retryOptions retry options
*/
export async function retryAsync<T>(
fn: () => Promise<T>,
opts: RetryOptions,
): Promise<T> {
try {
return await fn();
} catch (err) {
if (opts.times > 1) {
await wait(opts.delay);
opts.times -= 1;
return await retryAsync(fn, opts);
}
throw err;
}
}
function needsRetry(res: Response): boolean {
return 500 <= res.status && res.status < 600 && res.status !== 501;
}
/**
* Fetch a resource from the network. It returns a Promise that resolves to the
* Response to that request, whether it is successful or not.
*
* Unlike fetch, fetchRetry will retry a request on error or if the response is 50x but not 501.
* Also unlike fetch, fetchRetry will reject the promise if the response is not OK.
*
* const response = await fetch("http://my.json.host/data.json");
* console.log(response.status); // e.g. 200
* console.log(response.statusText); // e.g. "OK"
* const jsonData = await response.json();
*/
export async function fetchRetry(
input: string | Request | URL,
init?: RequestInit,
retryOpts: RetryOptions = { delay: 4000, times: 5 },
): Promise<Response> {
const fn = async () => {
return fetch(input, init).then((res) => {
if (needsRetry(res)) {
throw res;
}
return res;
});
};
if (!retryOpts.delay || !retryOpts.times) {
throw new TypeError(
"If retryOpts is specified, both delay and times must be declared.",
);
}
return retryAsync(fn, retryOpts).then((res) => {
if (res.ok === false) {
throw res;
}
return res;
});
}
/**
* Retry options:
* - maxTry: maximum number of attempts. if fn is still throwing execption afect maxtry attempts, an exepction is thrown
* - delay: number of miliseconds between each attempt.
*/
export interface RetryOptions {
times: number; // maximum number of attempts. if fn is still throwing execption afect maxtry attempts, an exepction is thrown
delay: number; //number of miliseconds between each attempt.
}