UNPKG

2.08 kBJavaScriptView Raw
1import retry from 'retry';
2import isNetworkError from 'is-network-error';
3
4export class AbortError extends Error {
5 constructor(message) {
6 super();
7
8 if (message instanceof Error) {
9 this.originalError = message;
10 ({message} = message);
11 } else {
12 this.originalError = new Error(message);
13 this.originalError.stack = this.stack;
14 }
15
16 this.name = 'AbortError';
17 this.message = message;
18 }
19}
20
21const decorateErrorWithCounts = (error, attemptNumber, options) => {
22 // Minus 1 from attemptNumber because the first attempt does not count as a retry
23 const retriesLeft = options.retries - (attemptNumber - 1);
24
25 error.attemptNumber = attemptNumber;
26 error.retriesLeft = retriesLeft;
27 return error;
28};
29
30export default async function pRetry(input, options) {
31 return new Promise((resolve, reject) => {
32 options = {
33 onFailedAttempt() {},
34 retries: 10,
35 ...options,
36 };
37
38 const operation = retry.operation(options);
39
40 const abortHandler = () => {
41 operation.stop();
42 reject(options.signal?.reason);
43 };
44
45 if (options.signal && !options.signal.aborted) {
46 options.signal.addEventListener('abort', abortHandler, {once: true});
47 }
48
49 const cleanUp = () => {
50 options.signal?.removeEventListener('abort', abortHandler);
51 operation.stop();
52 };
53
54 operation.attempt(async attemptNumber => {
55 try {
56 const result = await input(attemptNumber);
57 cleanUp();
58 resolve(result);
59 } catch (error) {
60 try {
61 if (!(error instanceof Error)) {
62 throw new TypeError(`Non-error was thrown: "${error}". You should only throw errors.`);
63 }
64
65 if (error instanceof AbortError) {
66 throw error.originalError;
67 }
68
69 if (error instanceof TypeError && !isNetworkError(error)) {
70 throw error;
71 }
72
73 await options.onFailedAttempt(decorateErrorWithCounts(error, attemptNumber, options));
74
75 if (!operation.retry(error)) {
76 throw operation.mainError();
77 }
78 } catch (finalError) {
79 decorateErrorWithCounts(finalError, attemptNumber, options);
80 cleanUp();
81 reject(finalError);
82 }
83 }
84 });
85 });
86}