Skip to content

Commit 0199995

Browse files
committed
feat: fixed issues #1,#2,#3 and #4
1 parent e3dd801 commit 0199995

File tree

3 files changed

+80
-69
lines changed

3 files changed

+80
-69
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "redo.js",
3-
"version": "1.0.1",
3+
"version": "1.2.0",
44
"description": "A simple but powerful library for your retrying operations.",
55
"main": "dist/index.js",
66
"types": "dist/index.d.ts",

src/index.ts

+1-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1 @@
1-
export {
2-
retryOperation,
3-
retryAsyncOperation,
4-
RetryAsyncOperation,
5-
RetryOperation,
6-
} from "./lib/retryOperation";
1+
export { retryOperation, RetryOperation } from "./lib/retryOperation";

src/lib/retryOperation.ts

+78-62
Original file line numberDiff line numberDiff line change
@@ -1,103 +1,119 @@
1-
interface RetryOperation {
2-
retryCount: number | "infinite";
3-
retryDelay: number;
4-
retryCallback: (payload?: any) => any;
5-
onErrorCallback: (error?: Error, currentRetryCount?: number) => void;
6-
onSuccessCallback: (response?: any) => void;
7-
afterLastAttemptErrorCallback?: (error?: any) => void;
1+
interface RetryOperation<TResponse> {
2+
retryCount?: number | "infinite";
3+
retryDelay?: number;
4+
retryCallback?: (payload?: any) => TResponse | Promise<TResponse>;
5+
onErrorCallback?: (
6+
error?: Error,
7+
currentRetryCount?: number
8+
) => void | Promise<void>;
9+
onSuccessCallback?: (response: TResponse) => void | Promise<void>;
10+
afterLastAttemptErrorCallback?: (error?: any) => void | Promise<void>;
811
incrementalDelayFactor?: number; // Optional factor to increase the delay
12+
logCallback?: (message: string) => void; // Optional logging mechanism
13+
enableLogging?: boolean; // Enable or disable logging
14+
retryCondition?: RetryCondition; // Custom condition to decide retry continuation
915
}
1016

11-
interface RetryAsyncOperationExtended extends RetryOperation {
12-
retryAsyncCallback: () => Promise<void>;
13-
}
17+
type RetryCondition = (currentRetryCount: number, lastError: any) => boolean;
1418

15-
type RetryAsyncOperation = Omit<RetryAsyncOperationExtended, "retryCallback">;
19+
const MAX_DELAY = 30000; // Maximum delay in milliseconds
1620

1721
// Utility function to introduce a delay
1822
const sleep = (delay: number): Promise<void> => {
1923
return new Promise((resolve) => setTimeout(resolve, delay));
2024
};
2125

22-
// Synchronous retry operation with error handling and exponential delay
23-
async function retryOperation({
26+
/**
27+
* Retries a callback function with error handling and exponential delay.
28+
* Supports both synchronous and asynchronous functions.
29+
*
30+
* @param retryCallback - The function to retry (sync/async).
31+
* @param onErrorCallback - Called on each retry failure.
32+
* @param onSuccessCallback - Called on successful retry.
33+
* @param retryCount - Maximum retry attempts or "infinite".
34+
* @param retryDelay - Initial retry delay in milliseconds.
35+
* @param incrementalDelayFactor - Multiplier for delay after each retry.
36+
* @param afterLastAttemptErrorCallback - Called after the last failed attempt.
37+
* @param logCallback - Optional logging mechanism for debugging.
38+
* @param enableLogging - Enables/disables logging (default: true).
39+
* @param retryCondition - Custom condition to decide if retry should continue.
40+
*/
41+
async function retryOperation<TResponse>({
2442
retryCallback,
2543
onErrorCallback,
2644
onSuccessCallback,
2745
afterLastAttemptErrorCallback,
2846
retryCount = 3, // Default retry count is 3
2947
retryDelay = 1000, // Default is 1 second
3048
incrementalDelayFactor = 1.5, // Default factor is 1.5
31-
}: RetryOperation): Promise<void> {
49+
logCallback,
50+
enableLogging = true, // Default to true
51+
retryCondition,
52+
}: RetryOperation<TResponse>): Promise<void> {
3253
let currentRetryCount = 0;
3354
let lastError: any = null;
3455
let currentDelay = retryDelay;
3556

36-
// Loop until retries are exhausted or successful
37-
while (retryCount === "infinite" || currentRetryCount <= retryCount) {
38-
try {
39-
if (currentDelay > 0 && currentRetryCount > 0) {
40-
await sleep(currentDelay); // Wait for the delay if it's not the first attempt
41-
}
42-
43-
const response = retryCallback();
44-
onSuccessCallback(response); // Call success callback on success
45-
return;
46-
} catch (error) {
47-
lastError = error;
48-
onErrorCallback(error as Error, currentRetryCount); // Handle error with retry count
49-
currentDelay *= incrementalDelayFactor; // Increase the delay for the next retry
50-
currentRetryCount++;
57+
// Helper function to log messages if logging is enabled
58+
const log = (message: string) => {
59+
if (enableLogging && logCallback) {
60+
logCallback(message);
5161
}
52-
}
53-
54-
// Call the final error callback if retries are exhausted
55-
if (afterLastAttemptErrorCallback) {
56-
afterLastAttemptErrorCallback(lastError);
57-
}
58-
}
59-
60-
// Asynchronous retry operation with error handling and exponential delay
61-
async function retryAsyncOperation({
62-
retryAsyncCallback,
63-
onErrorCallback,
64-
onSuccessCallback,
65-
afterLastAttemptErrorCallback,
66-
retryCount = 3, // Default retry count is 3
67-
retryDelay = 1000, // Default is 1 second
68-
incrementalDelayFactor = 1.5, // Default factor is 1.5
69-
}: RetryAsyncOperation): Promise<void> {
70-
let currentRetryCount = 0;
71-
let lastError: any = null;
72-
let currentDelay = retryDelay;
62+
};
7363

7464
// Loop until retries are exhausted or successful
75-
while (retryCount === "infinite" || currentRetryCount <= retryCount) {
65+
while (
66+
(retryCount === "infinite" || currentRetryCount <= retryCount) &&
67+
(!retryCondition || retryCondition(currentRetryCount, lastError))
68+
) {
7669
try {
7770
if (currentDelay > 0 && currentRetryCount > 0) {
71+
log(`Attempt ${currentRetryCount}: Waiting for ${currentDelay} ms`);
7872
await sleep(currentDelay); // Wait for the delay if it's not the first attempt
7973
}
8074

81-
const response = await retryAsyncCallback();
82-
onSuccessCallback(response); // Call success callback on success
75+
log(`Attempt ${currentRetryCount}: Executing callback`);
76+
const response = await retryCallback();
77+
78+
log(`Attempt ${currentRetryCount}: Success`);
79+
if (onSuccessCallback) {
80+
try {
81+
const successResponse = onSuccessCallback(response);
82+
if (successResponse instanceof Promise) await successResponse;
83+
} catch (successCallbackError) {
84+
log(`Error in onSuccessCallback: ${successCallbackError}`);
85+
}
86+
}
8387
return;
8488
} catch (error) {
8589
lastError = error;
86-
onErrorCallback(error as Error, currentRetryCount); // Handle error with retry count
87-
currentDelay *= incrementalDelayFactor; // Increase the delay for the next retry
90+
log(`Attempt ${currentRetryCount}: Failed with error: ${error}`);
91+
if (onErrorCallback) {
92+
try {
93+
const errorResponse = onErrorCallback(
94+
error as Error,
95+
currentRetryCount
96+
);
97+
if (errorResponse instanceof Promise) await errorResponse;
98+
} catch (callbackError) {
99+
log(`Error in onErrorCallback: ${callbackError}`);
100+
}
101+
}
102+
currentDelay = Math.min(currentDelay * incrementalDelayFactor, MAX_DELAY); // Increase delay
88103
currentRetryCount++;
89104
}
90105
}
91106

92107
// Call the final error callback if retries are exhausted
93108
if (afterLastAttemptErrorCallback) {
94-
afterLastAttemptErrorCallback(lastError);
109+
try {
110+
log(`Retries exhausted. Invoking afterLastAttemptErrorCallback`);
111+
const finalErrorResponse = afterLastAttemptErrorCallback(lastError);
112+
if (finalErrorResponse instanceof Promise) await finalErrorResponse;
113+
} catch (finalCallbackError) {
114+
log(`Error in afterLastAttemptErrorCallback: ${finalCallbackError}`);
115+
}
95116
}
96117
}
97118

98-
export {
99-
retryOperation,
100-
retryAsyncOperation,
101-
RetryOperation,
102-
RetryAsyncOperation,
103-
};
119+
export { retryOperation, RetryOperation, RetryCondition };

0 commit comments

Comments
 (0)