useAsyncData Composable
Enhanced async data fetching with caching, retry and abort controller
Code
import { ref, type Ref, onUnmounted } from 'vue';
interface UseAsyncDataOptions<T> {
cache?: boolean;
cacheKey?: string;
retry?: number;
retryDelay?: number;
immediate?: boolean;
}
const cache = new Map<string, any>();
export function useAsyncData<T>(
fetcher: (signal: AbortSignal) => Promise<T>,
options: UseAsyncDataOptions<T> = { cache: false, retry: 0, retryDelay: 1000, immediate: true }
): {
data: Ref<T | null>;
loading: Ref<boolean>;
error: Ref<Error | null>;
refresh: () => Promise<T | null>;
abort: () => void;
} {
const data = ref<T | null>(null) as Ref<T | null>;
const loading = ref(false);
const error = ref<Error | null>(null);
let abortController: AbortController | null = null;
const abort = () => {
__TOKEN_25__ (abortController) {
abortController.abort();
abortController = null;
}
loading.value = false;
};
const refresh = __TOKEN_27__ (attempt = 0): Promise<T | null> => {
// Use cache if available
__TOKEN_28__ (options.cache && options.cacheKey && cache.has(options.cacheKey)) {
data.value = cache.get(options.cacheKey);
return data.value;
}
abort();
abortController = new AbortController();
loading.value = true;
error.value = null;
try {
const result = await fetcher(abortController.signal);
data.value = result;
// Cache the result
__TOKEN_34__ (options.cache && options.cacheKey) {
cache.set(options.cacheKey, result);
}
return result;
} __TOKEN_36__ (e) {
error.value = e instanceof Error ? e : new Error('Unknown error');
// Retry if needed
__TOKEN_39__ (attempt < options.retry!) {
await new __TOKEN_82__(resolve => setTimeout(resolve, options.retryDelay!));
return refresh(attempt + 1);
}
return null;
} finally {
loading.value = false;
}
};
// Immediate fetch
__TOKEN_45__ (options.immediate) {
refresh();
}
// Cleanup on unmount
onUnmounted(() => {
abort();
});
return { data, loading, error, refresh, abort };
}
// Usage example
// const { data, loading, error, refresh } = useAsyncData(
// (signal) => fetch('/api/data', { signal }).then(r => r.json()),
// { cache: true, cacheKey: 'api-data', retry: 2 }
// );