Last active
February 4, 2025 22:38
-
-
Save trezy/5f2d5738c551765544dcd8b8500ab1b8 to your computer and use it in GitHub Desktop.
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
type UnknownArgs = Array<unknown> | |
type UnknownCallback = (...args: Array<unknown>) => unknown | |
export class ExecutionCache { | |
/** The string used to separate keys in a cache combined key. */ | |
#cacheKeySeparator = '::' | |
/** Stores all cache items. */ | |
#cache: Map<string, unknown> = new Map | |
/** Stores arg keys. */ | |
#keys: WeakMap<WeakKey, string> = new Map | |
/** These value types will be used as their own keys. */ | |
#selfKeys = [ | |
'bigint', | |
'number', | |
'string', | |
] | |
/** These value types will be stringified and used as their own keys. */ | |
#stringifiedKeys = [ | |
'boolean', | |
'undefined', | |
] | |
/** | |
* Retrieves keys for an array of args. | |
* | |
* @param args The arguments to retrieve keys for. | |
* @param createIfMissing Whether to create keys for arguments if they don't currently exist in the cache. | |
*/ | |
#getKeys(args: UnknownArgs, createIfMissing: boolean = false) { | |
return args.map(arg => { | |
if (this.#selfKeys.includes(typeof arg)) { | |
return arg | |
} | |
if (this.#stringifiedKeys.includes(typeof arg)) { | |
return String(arg) | |
} | |
if (arg === null) { | |
return 'null' | |
} | |
if (this.#keys.has(arg as WeakKey)) { | |
return this.#keys.get(arg as WeakKey) | |
} | |
if (createIfMissing) { | |
this.#keys.set(arg as WeakKey, crypto.randomUUID()) | |
return this.#keys.get(arg as WeakKey) | |
} | |
return null | |
}) | |
} | |
/** | |
* Returns a cached result if it receives the same function and arguments. If there is no cached result, execute the callback and cache the result. | |
* | |
* @param callback The callback to be executed. | |
* @param args The arguments that will be provided to the callback. | |
*/ | |
execute(callback: UnknownCallback, ...args: UnknownArgs) { | |
const cachedResult = this.get(callback, ...args) | |
if (cachedResult) { | |
return cachedResult | |
} | |
const result = callback(...args) | |
this.set(result, callback, ...args) | |
return result | |
} | |
/** | |
* Retrieves a cached execution result if it exists. | |
* | |
* @param args The arguments from which the cache key will be generated. | |
* @returns The cached result if it exists, otherwise null. | |
*/ | |
get(...args: UnknownArgs) { | |
const enums = this.#getKeys(args) | |
const isMissingEnums = enums.filter(Boolean).length === 0 | |
if (isMissingEnums) { | |
return null | |
} | |
return this.#cache.get(enums.join(this.#cacheKeySeparator)) | |
} | |
/** | |
* Stores an execution result in the cache. | |
* | |
* @param value The execution result. | |
* @param args The arguments from which the cache key will be generated. | |
* @returns The cached result if it exists, otherwise null. | |
*/ | |
set(value: unknown, ...args: UnknownArgs) { | |
const enums = this.#getKeys(args, true) | |
this.#cache.set(enums.join(this.#cacheKeySeparator), value) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment