Last active
May 1, 2021 08:52
-
-
Save mieky/81691cd47a18d4c3a33278ea89fad006 to your computer and use it in GitHub Desktop.
Simple function invocation memoization with Typescript
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as crypto from "crypto"; | |
type CacheKey = string; | |
type ExpiryTimestamp = number; | |
const log = (...args: any[]) => process.env.DEBUG && console.log(...args); | |
const hashCode = (input: string): string => | |
crypto.createHash("sha256").update(input).digest("hex"); | |
/** | |
* Returns a cache key for an invocation of a function (name + args): | |
* "handleAppMention__b28c94b2195c8ed259f0b415aaee3f39b0b2920a4537611499fa044956917a21". | |
* | |
* @param fn Function being called | |
* @param args Arguments the function is being called with | |
* @returns A cache key for a function invocation | |
*/ | |
const getCacheKey = (fn: Function, args: any[] = []): CacheKey => { | |
const fnName = fn.name || hashCode(fn.toString()); | |
const argsHash = args.map((a) => hashCode(JSON.stringify(a))).join("__"); | |
return `${fnName}__${argsHash}`; | |
}; | |
/** | |
* Memoize the return values returned by a given function. | |
* | |
* @param fn Function to memoize | |
* @param duration Number of milliseconds after which the result is no longer valid (optional) | |
* @returns A memoized version of the given function | |
*/ | |
function memoize<T extends Function>(fn: T, duration?: number): T { | |
const cache: Record<CacheKey, T> = {}; | |
const expiries: Record<CacheKey, ExpiryTimestamp> = {}; | |
const memoized = (...args: any[]): T => { | |
const cacheKey = getCacheKey(fn, args); | |
if (cache[cacheKey]) { | |
if (!expiries[cacheKey] || expiries[cacheKey] > Date.now()) { | |
log("found entry in cache for", cacheKey); | |
return cache[cacheKey]; | |
} | |
log("cache key expired, re-evaluate", cacheKey); | |
delete expiries[cacheKey]; | |
} | |
log("fresh run for cache key...", cacheKey); | |
const result = fn.apply(fn, args); | |
cache[cacheKey] = result; | |
if (duration) { | |
expiries[cacheKey] = new Date(Date.now() + duration).getTime(); | |
} | |
return result; | |
}; | |
return <T>(memoized as unknown); | |
} | |
export { memoize }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment