@oxyhq/services
Version:
Reusable OxyHQ module to handle authentication, user management, karma system, device-based session management and more 🚀
207 lines (192 loc) • 5.55 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.batchAsync = batchAsync;
exports.createCancellableAsync = createCancellableAsync;
exports.debounceAsync = debounceAsync;
exports.delay = void 0;
exports.parallelWithErrorHandling = parallelWithErrorHandling;
exports.retryAsync = retryAsync;
exports.retryOnError = retryOnError;
exports.sequentialWithProgress = sequentialWithProgress;
exports.throttleAsync = throttleAsync;
exports.withErrorHandling = withErrorHandling;
exports.withLoadingState = withLoadingState;
exports.withTimeout = withTimeout;
var _loggerUtils = require("./loggerUtils");
/**
* Async utilities for common asynchronous patterns and error handling
*/
/**
* Wrapper for async operations with automatic error handling
* Returns null on error instead of throwing
*/
async function withErrorHandling(operation, errorHandler, context) {
try {
return await operation();
} catch (error) {
if (errorHandler) {
errorHandler(error);
} else {
_loggerUtils.logger.error(`Error in ${context || 'operation'}`, error instanceof Error ? error : new Error(String(error)), {
component: 'asyncUtils',
method: 'withErrorHandling'
});
}
return null;
}
}
/**
* Execute multiple async operations in parallel with error handling
*/
async function parallelWithErrorHandling(operations, errorHandler) {
const results = await Promise.allSettled(operations.map((op, index) => withErrorHandling(op, error => errorHandler?.(error, index))));
return results.map(result => result.status === 'fulfilled' ? result.value : null);
}
/**
* Retry an async operation with exponential backoff
*
* By default, does not retry on 4xx errors (client errors).
* Use shouldRetry callback to customize retry behavior.
*/
async function retryAsync(operation, maxRetries = 3, baseDelay = 1000, shouldRetry) {
let lastError;
// Default shouldRetry: don't retry on 4xx errors
const defaultShouldRetry = error => {
// Don't retry on 4xx errors (client errors)
if (error?.response?.status >= 400 && error?.response?.status < 500) {
return false;
}
return true;
};
const retryCheck = shouldRetry || defaultShouldRetry;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
lastError = error;
if (attempt === maxRetries) {
break;
}
if (!retryCheck(error)) {
break;
}
// Calculate delay with exponential backoff and jitter
const delay = baseDelay * Math.pow(2, attempt) + Math.random() * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError;
}
/**
* Debounce async function calls
*/
function debounceAsync(func, delay) {
let timeoutId;
const lastPromise = null;
return (...args) => {
return new Promise((resolve, reject) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(async () => {
try {
const result = await func(...args);
resolve(result);
} catch (error) {
reject(error);
}
}, delay);
});
};
}
/**
* Throttle async function calls
*/
function throttleAsync(func, limit, interval) {
let inThrottle = false;
let lastPromise = null;
return (...args) => {
if (inThrottle) {
return lastPromise;
}
inThrottle = true;
lastPromise = func(...args);
setTimeout(() => {
inThrottle = false;
}, interval);
return lastPromise;
};
}
/**
* Execute async operations sequentially with progress tracking
*/
async function sequentialWithProgress(operations, onProgress) {
const results = [];
for (let i = 0; i < operations.length; i++) {
const result = await operations[i]();
results.push(result);
onProgress?.(i + 1, operations.length);
}
return results;
}
/**
* Batch async operations
*/
async function batchAsync(items, batchSize, processor) {
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
await processor(batch);
}
}
/**
* Create a cancellable async operation
*/
function createCancellableAsync(operation) {
let abortController = null;
return {
execute: async () => {
abortController = new AbortController();
return await operation(abortController.signal);
},
cancel: () => {
abortController?.abort();
}
};
}
/**
* Timeout wrapper for async operations
*/
async function withTimeout(operation, timeoutMs, timeoutMessage) {
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => {
reject(new Error(timeoutMessage || `Operation timed out after ${timeoutMs}ms`));
}, timeoutMs);
});
return Promise.race([operation, timeoutPromise]);
}
/**
* Execute async operation with loading state
*/
async function withLoadingState(operation, setLoading) {
setLoading(true);
try {
return await operation();
} finally {
setLoading(false);
}
}
/**
* Create a promise that resolves after a delay
*/
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
/**
* Execute async operation with retry on specific errors
*/
exports.delay = delay;
async function retryOnError(operation, retryableErrors, maxRetries = 3) {
return retryAsync(operation, maxRetries, 1000, error => {
const errorCode = error?.code || error?.status || error?.message;
return retryableErrors.includes(errorCode);
});
}
//# sourceMappingURL=asyncUtils.js.map