Hey folks, I’m wiring up a tiny fetch wrapper for a web app and I’m trying to keep it simple: cache responses, retry on flaky networks, and dedupe in-flight requests. The failure mode I keep seeing is the UI briefly showing old data even after the refetch resolves, which feels like a race I’m accidentally creating.
const cache = new Map();
const inflight = new Map();
export async function getJSON(url, { ttlMs = 5000, retries = 2 } = {}) {
const now = Date.now();
const hit = cache.get(url);
if (hit && now - hit.t < ttlMs) return hit.v;
if (inflight.has(url)) return inflight.get(url);
const p = (async () => {
let lastErr;
for (let i = 0; i <= retries; i++) {
try {
const res = await fetch(url, { cache: "no-store" });
if (!res.ok) throw new Error(res.status);
const v = await res.json();
cache.set(url, { t: Date.now(), v });
return v;
} catch (e) {
lastErr = e;
}
}
throw lastErr;
})().finally(() => inflight.delete(url));
inflight.set(url, p);
return p;
}
What’s the safest way to structure this so a late retry or an older in-flight request can’t overwrite newer cached data and cause that stale flash?
Sarah ![]()