1 | export function delayedPromise(delay: number): Promise<void> {
|
2 | return new Promise(resolve => setTimeout(resolve, delay));
|
3 | }
|
4 |
|
5 | export function timeoutPromise<T>(promise: Promise<T>, ms: number, message = `timed out after ${ms}ms`): Promise<T> {
|
6 | return new Promise(function (resolve, reject) {
|
7 | setTimeout(() => reject(new Error(message)), ms);
|
8 | promise.then(resolve, reject);
|
9 | });
|
10 | }
|
11 |
|
12 | export interface RetryPromiseOptions {
|
13 | interval: number;
|
14 | retries: number;
|
15 | timeout?: number;
|
16 | timeoutMessage?: string;
|
17 | }
|
18 |
|
19 | const never: Promise<any> = new Promise(() => void 0);
|
20 |
|
21 | export async function retryPromise<T>(promiseProvider: () => Promise<T>,
|
22 | {interval, retries, timeout, timeoutMessage = `timed out after ${timeout}ms`}: RetryPromiseOptions): Promise<T> {
|
23 | if (!timeout && !retries) {
|
24 | return await promiseProvider();
|
25 | }
|
26 | if (timeout && timeout <= retries * interval) {
|
27 | return Promise.reject(`timeout (${timeout}ms) must be greater than retries (${retries}) times interval (${interval}ms)`)
|
28 | }
|
29 | const raceWithTimeout = [never, never];
|
30 | if (timeout) {
|
31 | raceWithTimeout[1] = delayedPromise(timeout).then(() => Promise.reject(new Error(timeoutMessage)));
|
32 | }
|
33 | let iterations = retries ? retries + 1 : Number.MAX_SAFE_INTEGER;
|
34 | let lastError: Error | null = null;
|
35 | while (iterations-- > 0) {
|
36 | try {
|
37 | raceWithTimeout[0] = promiseProvider();
|
38 | return await Promise.race(raceWithTimeout);
|
39 | } catch (e) {
|
40 | if (e.message === timeoutMessage) {
|
41 | throw lastError || e;
|
42 | }
|
43 | lastError = e;
|
44 | }
|
45 | await new Promise(resolve => setTimeout(resolve, interval));
|
46 | }
|
47 | throw lastError || new Error(timeoutMessage);
|
48 | }
|