Skip to content

Instantly share code, notes, and snippets.

@ccnokes
Last active November 1, 2020 18:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ccnokes/b972aaed452b507749cf1013082296a9 to your computer and use it in GitHub Desktop.
Save ccnokes/b972aaed452b507749cf1013082296a9 to your computer and use it in GitHub Desktop.
A memoize function is only as good as its cache
// NOTE can't use lodash.memoize because we need 2 caches and v2 doesn't support replacing the cache implementation
// TS type note: the below type is inspired by Lodash v4 types
function memoize<T extends (...args: any) => any>(fn: T, resolver?: (...args: any[]) => any) {
let objectCache = new WeakMap<any, any>(); // WeakMap can only store objects as keys and allows the objects to get GC'ed out
let primitiveCache = new Map<any, any>(); // Map can store anything as a key but no auto GC
return function(...args: Parameters<T>): ReturnType<T> {
// just in case things got out of hand, dump the cache if too many entries
if (primitiveCache.size > 10000) {
primitiveCache.clear();
}
let key = resolver ? resolver(args) : args[0];
let cache = typeof key === 'object' ? objectCache : primitiveCache;
if (cache.has(key)) {
// console.count(`cache hit`);
return cache.get(key);
} else {
// console.count(`cache miss`);
let result = fn.apply(null, args);
// don't allow empty/nulls/NaNs as keys
if (key != null && key !== '' && !(typeof key === 'number' && isNaN(key))) {
cache.set(key, result);
}
return result;
}
};
}
// this memoize better supports use cases like this out of the box
// it'll cache the string in the primitive cache and the Thing in objectCache
let doStuff = memoize((stringOrThing: string | Thing) => { /*...*/ });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment