120 lines
3.5 KiB
JavaScript
120 lines
3.5 KiB
JavaScript
const { logError } = require('./logger');
|
|
|
|
/**
|
|
* Wraps an async function to ensure errors are caught and logged
|
|
* @param {Function} fn - Async function to wrap
|
|
* @param {string} context - Context name for logging
|
|
* @returns {Function} - Wrapped function
|
|
*/
|
|
function wrapAsync(fn, context = 'Unknown') {
|
|
return async (...args) => {
|
|
try {
|
|
return await fn(...args);
|
|
} catch (err) {
|
|
logError('AsyncError', `Unhandled error in ${context}: ${err.message}`);
|
|
logError('AsyncError', err.stack);
|
|
throw err;
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Wraps an async callback to ensure errors are caught
|
|
* @param {Function} fn - Async callback function
|
|
* @param {string} context - Context name for logging
|
|
* @returns {Function} - Wrapped callback
|
|
*/
|
|
function wrapAsyncCallback(fn, context = 'Unknown') {
|
|
return async (...args) => {
|
|
try {
|
|
return await fn(...args);
|
|
} catch (err) {
|
|
logError('AsyncError', `Unhandled error in callback ${context}: ${err.message}`);
|
|
logError('AsyncError', err.stack);
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Creates a promise that never rejects (catches all errors)
|
|
* @param {Promise} promise - Promise to wrap
|
|
* @param {string} context - Context for error logging
|
|
* @returns {Promise} - Promise that resolves with { success, result, error }
|
|
*/
|
|
async function safePromise(promise, context = 'Unknown') {
|
|
try {
|
|
const result = await promise;
|
|
return { success: true, result };
|
|
} catch (err) {
|
|
logError('AsyncError', `Error in safe promise ${context}: ${err.message}`);
|
|
return { success: false, error: err };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Executes multiple promises safely, collecting results and errors
|
|
* @param {Array<Promise>} promises - Array of promises
|
|
* @param {string} context - Context for error logging
|
|
* @returns {Promise<Array>} - Array of { success, result, error } objects
|
|
*/
|
|
async function safeAll(promises, context = 'Unknown') {
|
|
const results = await Promise.allSettled(promises);
|
|
return results.map((result, index) => {
|
|
if (result.status === 'fulfilled') {
|
|
return { success: true, result: result.value };
|
|
} else {
|
|
logError('AsyncError', `Error in safeAll promise ${index} (${context}): ${result.reason?.message || result.reason}`);
|
|
return { success: false, error: result.reason };
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Retries an async function with exponential backoff
|
|
* @param {Function} fn - Async function to retry
|
|
* @param {object} options - Retry options
|
|
* @param {number} options.maxRetries - Maximum number of retries
|
|
* @param {number} options.initialDelay - Initial delay in ms
|
|
* @param {number} options.maxDelay - Maximum delay in ms
|
|
* @param {Function} options.shouldRetry - Function to determine if error should be retried
|
|
* @returns {Promise} - Result of function call
|
|
*/
|
|
async function retryWithBackoff(fn, options = {}) {
|
|
const {
|
|
maxRetries = 3,
|
|
initialDelay = 1000,
|
|
maxDelay = 10000,
|
|
shouldRetry = () => true
|
|
} = options;
|
|
|
|
let lastError;
|
|
let delay = initialDelay;
|
|
|
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
try {
|
|
return await fn();
|
|
} catch (err) {
|
|
lastError = err;
|
|
|
|
if (attempt === maxRetries || !shouldRetry(err)) {
|
|
throw err;
|
|
}
|
|
|
|
logError('Retry', `Attempt ${attempt + 1}/${maxRetries + 1} failed: ${err.message}. Retrying in ${delay}ms...`);
|
|
await new Promise(resolve => setTimeout(resolve, delay));
|
|
delay = Math.min(delay * 2, maxDelay);
|
|
}
|
|
}
|
|
|
|
throw lastError;
|
|
}
|
|
|
|
module.exports = {
|
|
wrapAsync,
|
|
wrapAsyncCallback,
|
|
safePromise,
|
|
safeAll,
|
|
retryWithBackoff
|
|
};
|
|
|