-
Notifications
You must be signed in to change notification settings - Fork 210
Expand file tree
/
Copy pathwidgetFetchRetry.ts
More file actions
88 lines (74 loc) · 2.04 KB
/
Copy pathwidgetFetchRetry.ts
File metadata and controls
88 lines (74 loc) · 2.04 KB
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
83
84
85
86
87
88
export const WIDGET_FETCH_RETRY_CONFIG: {
maxRetries: number;
baseBackoffMs: number;
} = {
maxRetries: 3,
baseBackoffMs: 300,
};
export interface WidgetFetchRetryOptions<T> {
load: () => Promise<T>;
signal?: AbortSignal;
maxRetries?: number;
baseBackoffMs?: number;
}
export function computeWidgetFetchRetryDelay(
retryIndex: number,
baseBackoffMs = WIDGET_FETCH_RETRY_CONFIG.baseBackoffMs
): number {
return baseBackoffMs * 2 ** retryIndex;
}
function toAbortError(signal?: AbortSignal): Error {
const reason = signal?.reason;
if (reason instanceof Error) {
return reason;
}
return new DOMException('The operation was aborted.', 'AbortError');
}
function isAbortError(error: unknown): boolean {
return error instanceof DOMException
? error.name === 'AbortError'
: error instanceof Error && error.name === 'AbortError';
}
function delay(ms: number, signal?: AbortSignal): Promise<void> {
return new Promise((resolve, reject) => {
if (signal?.aborted) {
reject(toAbortError(signal));
return;
}
const timeoutId = window.setTimeout(() => {
signal?.removeEventListener('abort', onAbort);
resolve();
}, ms);
const onAbort = () => {
window.clearTimeout(timeoutId);
signal?.removeEventListener('abort', onAbort);
reject(toAbortError(signal));
};
signal?.addEventListener('abort', onAbort);
});
}
export async function runWidgetFetchWithRetry<T>({
load,
signal,
maxRetries = WIDGET_FETCH_RETRY_CONFIG.maxRetries,
baseBackoffMs = WIDGET_FETCH_RETRY_CONFIG.baseBackoffMs,
}: WidgetFetchRetryOptions<T>): Promise<T> {
let retryCount = 0;
for (;;) {
if (signal?.aborted) {
throw toAbortError(signal);
}
try {
return await load();
} catch (error) {
if (signal?.aborted || isAbortError(error)) {
throw toAbortError(signal);
}
if (retryCount >= maxRetries) {
throw error;
}
await delay(computeWidgetFetchRetryDelay(retryCount, baseBackoffMs), signal);
retryCount += 1;
}
}
}