VUE

useAsyncData Composable

Enhanced async data fetching with caching, retry and abort controller

Vue3ComposablesAsyncDataFetchCaching

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 }
// );