memo
Caches a function’s return value per unique argument set so repeat calls skip the computation. Optionally expires entries after ttl milliseconds, and accepts a custom key resolver for control over cache identity.
Try it
Section titled “Try it”Import
Section titled “Import”import { memo } from "1o1-utils";import { memo } from "1o1-utils/memo";Signature
Section titled “Signature”function memo<T extends (...args: never[]) => unknown>(params: { fn: T; ttl?: number; key?: (args: Parameters<T>) => unknown;}): T & { clear: () => void; delete: (key: unknown) => boolean; readonly size: number;}Parameters
Section titled “Parameters”| Name | Type | Required | Description |
|---|---|---|---|
fn | T | Yes | The function to memoize |
ttl | number | No | Entry lifetime in milliseconds (non-negative). Omit for no expiry. |
key | (args: Parameters<T>) => unknown | No | Resolver mapping the call arguments to a cache key. Omit to use the default resolver. |
Returns
Section titled “Returns”A memoized wrapper with the same call signature as fn, plus:
.clear()— empty the cache.delete(key)— remove a single entry; returnstrueif it existed.size— number of cached entries (read-only)
Examples
Section titled “Examples”const slow = (n: number) => { for (let i = 0; i < 1e7; i++); return n * 2;};
const fast = memo({ fn: slow });fast(5); // computesfast(5); // cached// Time-bounded cacheconst fetchUser = memo({ fn: getUser, ttl: 60_000 });
await fetchUser("alice"); // network callawait fetchUser("alice"); // cached for 60s// Normalize args via custom keyconst search = memo({ fn: (q: string) => index.query(q), key: (args) => args[0].toLowerCase().trim(),});
search("React");search("react ");search("REACT"); // all share one cache entry// Inspect and reset the cacheconst lookup = memo({ fn: (id: number) => db.get(id) });
lookup(1);lookup(2);lookup.size; // 2lookup.delete(1); // truelookup.clear();Edge Cases
Section titled “Edge Cases”- Throws if
fnis not a function. - Throws if
ttlis provided and is not a non-negative number (NaN rejected). - Throws if
keyis provided and is not a function. - The default key resolver is hybrid: when the call has a single primitive argument it uses that argument directly; otherwise it falls back to
JSON.stringify(args). Provide your ownkeyfor non-serializable args (functions, circular structures,BigInt,Symbol). - TTL is measured against
performance.now()and is monotonic — immune to system clock changes. - Expiry is lazy: stale entries are not actively swept; they are recomputed on the next call for that key.
- A return value of
undefinedis cached —fnwill not re-run. - If
fnthrows, the error propagates and the entry is NOT cached, so the next call retries. - The
thiscontext of each call is forwarded tofn.
Also known as
Section titled “Also known as”memoize, cache, function cache, ttl cache, result cache, lazy compute
Prompt suggestion
Section titled “Prompt suggestion”I'm using 1o1-utils (npm: https://www.npmjs.com/package/1o1-utils, GitHub: https://github.com/pedrotroccoli/1o1-utils, LLM context: https://pedrotroccoli.github.io/1o1-utils/llms.txt). Show me how to use memo to cache an expensive function with a TTL