AniUI Academy

Cache async results with a TTL

hardAsync & Promises+350 XPPractice

Config, feature flags and the current user are read constantly and change rarely. Caching them for a few seconds removes most of the requests, as long as concurrent callers share one load and errors are not remembered.

Implement createCache(loader, ttl), returning an object with get(key) and invalidate(key). On a miss, get calls loader(key) and returns its promise. While that load is in flight, further get calls for the same key return the same promise without calling loader again. Once it fulfils, the value is served for ttl milliseconds measured from when it fulfilled; after that the next get loads again. If the loader rejects, the rejection is passed to the waiting callers and nothing is stored, so the next get retries. invalidate(key) drops any entry for that key so the next get reloads.

Arguments

loader(key: any) => Promise<T>
Called on cache miss to fetch the value for key.
ttlnumber
How long a fulfilled value stays fresh, in milliseconds.

Returns

{ get: (key: any) => Promise<T>, invalidate: (key: any) => void } — A cache with a get that returns the cached (or loading) promise and an invalidate that drops an entry.

Examples

let calls = 0;
const cache = createCache(async (key) => {
  calls++;
  await new Promise((r) => setTimeout(r, 20));
  return "value:" + key;
}, 1000);

const [a, b, c] = await Promise.all([
  cache.get("x"), cache.get("x"), cache.get("x"),
]);
// a === b === c === "value:x", calls === 1 — one load shared by three callers

Concurrent callers for the same key share a single load.

What it has to do

  • Call loader once for a key and reuse the value until the TTL expires.
  • Share one in-flight load between concurrent get calls for the same key.
  • Reload after the TTL has elapsed.
  • Never cache a rejection: the next get must call loader again.
  • Make invalidate(key) force the next get to reload.

Notes

  • Store the promise (not the resolved value) so in-flight loads can be shared — callers that get during the load join the same promise.
  • Never cache a rejection: delete the entry in the error handler so the next get retries.

Stuck? The javascript course covers everything this challenge needs.

Try it yourself
Loading playground...
5 tests · +350 XP

Discussion

Sign in to post
Sign in to join the conversation.Sign in

No comments yet. Be the first to say something.