68 lines
1.6 KiB
JavaScript
68 lines
1.6 KiB
JavaScript
import { deepEqual } from '@/utils/deepEqual'
|
||
import { executeRequest } from './executeRequest'
|
||
|
||
const cacheStore = new Map()
|
||
|
||
/**
|
||
* Cache-first, stale-while-revalidate data fetcher.
|
||
*
|
||
* Supports:
|
||
* - URL string (GET)
|
||
* - Request object (GET / POST)
|
||
* - Custom async fetcher function
|
||
*
|
||
* @param {Object} options
|
||
* @param {string} options.key - Unique cache key
|
||
* @param {string|Object} [options.request] - URL or request config
|
||
* @param {Function} [options.fetcher] - Custom async function
|
||
* @param {Function} options.onUpdate - Callback with data
|
||
*
|
||
* @example
|
||
* fetchWithCache({
|
||
* key: 'users.list',
|
||
* request: '/api/users',
|
||
* onUpdate: (data, source) => {
|
||
* users.value = data
|
||
* }
|
||
* })
|
||
*/
|
||
export async function fetchWithCache({
|
||
key,
|
||
request = null,
|
||
fetcher = null,
|
||
onUpdate = null
|
||
}) {
|
||
if (!key) {
|
||
throw new Error('fetchWithCache requires a cache key')
|
||
}
|
||
|
||
// 1️⃣ Emit cached data immediately
|
||
if (cacheStore.has(key)) {
|
||
onUpdate?.(cacheStore.get(key), 'cache')
|
||
}
|
||
|
||
// 2️⃣ Build fetcher if not provided
|
||
let resolvedFetcher = fetcher
|
||
|
||
if (!resolvedFetcher && request) {
|
||
resolvedFetcher = () => executeRequest(request)
|
||
}
|
||
|
||
if (typeof resolvedFetcher !== 'function') {
|
||
throw new Error('fetchWithCache requires either fetcher or request')
|
||
}
|
||
|
||
// 3️⃣ Fetch fresh data
|
||
try {
|
||
const fresh = await resolvedFetcher()
|
||
const cached = cacheStore.get(key)
|
||
|
||
if (!cached || !deepEqual(cached, fresh)) {
|
||
cacheStore.set(key, fresh)
|
||
onUpdate?.(fresh, 'fresh')
|
||
}
|
||
} catch (err) {
|
||
console.error('[fetchWithCache] fetch failed:', err)
|
||
}
|
||
}
|