Skip to content

Instantly share code, notes, and snippets.

@colelawrence
Last active January 18, 2024 17:34
Show Gist options
  • Save colelawrence/a9e763ed3151695940c569e4ae263eb6 to your computer and use it in GitHub Desktop.
Save colelawrence/a9e763ed3151695940c569e4ae263eb6 to your computer and use it in GitHub Desktop.
const MINUTE = 1000 * 60;
function asyncCachedFn<Args extends any[], Result>(options: {
fn: (...args: Args) => Promise<Result>;
key: (...args: Args) => string;
}) {
const cache: Map<
string,
{ computedAt: number; evict: any; result: Promise<Result> }
> = new Map();
const addToCache = (key: string, result: Promise<Result>) => {
cache.set(key, {
computedAt: Date.now(),
evict: setTimeout(() => cache.delete(key), 1000 * 60 * 15),
result,
});
result.catch((err) => {
const curr = cache.get(key);
if (curr?.result === result) {
clearTimeout(curr.evict);
// evict from cache on error
cache.delete(key);
}
});
return result;
};
return (...args: Args) => {
const key = options.key(...args);
if (!cache.has(key)) {
return addToCache(key, options.fn(...args));
}
const cached = cache.get(key)!;
const msInCache = Date.now() - cached.computedAt;
if (msInCache > 5 * MINUTE) {
// older than five minutes, evict and recompute
clearTimeout(cache.get(key)?.evict);
cache.delete(key);
return addToCache(key, options.fn(...args));
} else if (msInCache > 1 * MINUTE) {
// older than one minute, recompute, but return cached result
addToCache(key, options.fn(...args));
return cached.result; // return max five minute old result
} else {
// computed within the last minute, return cached result
return cached.result;
}
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment