Hey everyone, I’m wiring a tiny fetch wrapper for my app and I’m trying to do retry + cache for a sprite sheet JSON, but I keep hitting a race where an older retry finishes later and overwrites the newer cached response.
const cache = new Map();
export async function getJSON(url) {
const cached = cache.get(url);
if (cached) return cached;
const p = withRetry(() => fetch(url).then(r => r.json()), 2);
cache.set(url, p);
return p;
}
async function withRetry(fn, retries) {
let lastErr;
for (let i = 0; i <= retries; i++) {
try { return await fn(); } catch (e) { lastErr = e; }
}
throw lastErr;
}
What’s a clean way to make sure only the latest in-flight request can populate the cache (without leaking promises or breaking dedupe)?
Also worth adding an AbortController per URL so when a new fetch starts you abort the old one, which prevents the late response from ever resolving and trying to write. If you can’t abort, tag each request with a monotonically increasing version and only commit if it matches the latest version for that key.
Also: keep one in-flight promise per cache key and have everyone await it, so you don’t spin up overlapping retries that can “win” late and overwrite newer data.
Store a requestId (or timestamp) next to the cache entry, bump it when you kick off a fetch, and only write the response if its id still matches the latest one you recorded. That way a slow retry can’t clobber a newer value that already landed.