Memoize a synchronous or asynchronous function.
Using npm:
npm install @chriscdn/memoize
Using yarn:
yarn add @chriscdn/memoize
import { Memoize } from "@chriscdn/memoize";
The Memoize
function can be used to memoize a synchronous or asynchronous function.
The cache is powered by quick-lru. Each call to Memoize
creates a new cache instance.
The Memoize
function prevents duplicate evaluations by ensuring that multiple calls with the same cache key are processed only once. This includes asynchronous functions.
By default, the cache key is generated by calling JSON.stringify()
on the function arguments. This behavior can be customized (see below).
To memoize a function:
const _add = (x: number, y: number) => x + y;
const add = Memoize(_add);
The add
function has the same interface as _add
:
const result = add(5, 7);
// 12
You can also define the function in a single line:
const add = Memoize((x: number, y: number) => x + y);
Memoizing an asynchronous function is similar:
const _add = async (x: number, y: number) => x + y;
const add = Memoize(_add);
const result = await add(5, 7);
// 12
The Memoize
function accepts an options
parameter to control the cache behavior:
const add = Memoize(_add, options);
Available options (with defaults):
const options = {
// The maximum number of items in the cache.
maxSize: 1000,
// The maximum duration (in milliseconds) an item can remain in the cache. If set to `undefined`, the item will not expire due to time constraints.
maxAge: undefined,
// A synchronous function that returns `true` or `false` to determine whether to add the returnValue to the cache.
shouldCache: (returnValue: Return, key: string) => true,
// A synchronous function to generate a cache key (must return a string).
resolver: (...args) => JSON.stringify(args),
};
Note: Memoize
works with both synchronous and asynchronous functions. However, when using the shouldCache
callback with an asynchronous function, the returnValue
passed to shouldCache
is a Promise
. This is effectively useless since caching decisions should be based on the resolved value.
To address this, use MemoizeAsync
instead. It has the same interface as Memoize
, but works with asynchronous functions and passes the resolved value to shouldCache
.
import { MemoizeAsync } from "@chriscdn/memoize";
The underlying quick-lru instance is accessible via the .cache
property on the memoized function:
const add = Memoize(_add);
const result = await add(5, 7);
console.log(add.cache.size === 1);
// true
// Clear the cache
add.cache.clear();
The values null
and undefined
are cached by default, but this behavior can be adjusted using the shouldCache
option.
Class methods can also be memoized, but this requires overriding the method within the constructor. Ensure you bind the method to the instance to maintain the correct context.
Here's an example where the add
method is memoized by reassigning it within the constructor. This approach preserves access to instance properties (like count
) and maintains the correct method signature in TypeScript:
class AddClass {
count: number = 0;
constructor() {
this.add = Memoize(this.add.bind(this));
}
add(x: number, y: number) {
this.count += 1;
return x + y;
}
}
Each memoized method in each class instance maintains its own cache.
Run the tests using:
yarn test