feat(providence): allow to clear cache of memoized function

This commit is contained in:
Thijs Louisse 2024-05-17 20:32:15 +02:00
parent 1df3854196
commit 994a7b5266
2 changed files with 55 additions and 23 deletions

View file

@ -24,30 +24,33 @@ function createCachableArg(arg) {
/**
* @template T
* @type {<T extends Function>(functionToMemoize:T, opts?:{ storage?:object; }) => T}
* @type {<T extends Function>(functionToMemoize:T, opts?:{ cacheStorage?:object; }) => T & {clearCache:() => void}}
*/
export function memoize(functionToMemoize, { storage = {} } = {}) {
return /** @type {* & T} */ (
function memoizedFn() {
// eslint-disable-next-line prefer-rest-params
const args = [...arguments];
const shouldSerialize = args.some(isObject);
export function memoize(functionToMemoize, { cacheStorage = {} } = {}) {
function memoizedFn() {
// eslint-disable-next-line prefer-rest-params
const args = [...arguments];
const shouldSerialize = args.some(isObject);
const cachableArgs = shouldSerialize ? args.map(createCachableArg) : args;
// Allow disabling of cache for testing purposes
const cachableArgs = shouldSerialize ? args.map(createCachableArg) : args;
// Allow disabling of cache for testing purposes
// @ts-expect-error
if (shouldCache && cachableArgs in cacheStorage) {
// @ts-expect-error
if (shouldCache && cachableArgs in storage) {
// @ts-expect-error
return storage[cachableArgs];
}
// @ts-expect-error
const outcome = functionToMemoize.apply(this, args);
// @ts-expect-error
// eslint-disable-next-line no-param-reassign
storage[cachableArgs] = outcome;
return outcome;
return cacheStorage[cachableArgs];
}
);
// @ts-expect-error
const outcome = functionToMemoize.apply(this, args);
// @ts-expect-error
// eslint-disable-next-line no-param-reassign
cacheStorage[cachableArgs] = outcome;
return outcome;
}
memoizedFn.clearCache = () => {
// eslint-disable-next-line no-param-reassign
cacheStorage = {};
};
return /** @type {* & T & {clearCache:() => void}} */ (memoizedFn);
}
/**

View file

@ -201,13 +201,13 @@ describe('Memoize', () => {
sumCalled += 1;
return { ...a, ...b };
}
const sumMemoized = memoize(sum, { serializeObjects: true });
const sumMemoized = memoize(sum);
let sum2Called = 0;
function sum2(/** @type {object} a */ a, /** @type {object} a */ b) {
sum2Called += 1;
return { ...a, ...b };
}
const sum2Memoized = memoize(sum2, { serializeObjects: true });
const sum2Memoized = memoize(sum2);
expect(sumMemoized({ x: 1 }, { y: 2 })).to.deep.equal({ x: 1, y: 2 });
expect(sumCalled).to.equal(1);
@ -233,7 +233,7 @@ describe('Memoize', () => {
sumCalled += 1;
return { ...a, ...b };
}
const sumMemoized = memoize(sum, { serializeObjects: true });
const sumMemoized = memoize(sum);
// Put in cache for args combination
const result = sumMemoized({ x: 1 }, { y: 2 });
@ -313,4 +313,33 @@ describe('Memoize', () => {
expect(sum2Called).to.equal(1);
});
});
describe('Cache', () => {
it(`"memoizedFn.clearCache()" clears the cache for a memoized fn"`, async () => {
let sumCalled = 0;
function sum(/** @type {string} a */ a, /** @type {string} a */ b) {
sumCalled += 1;
return a + b;
}
const sumMemoized = memoize(sum);
// Put in cache for args combination
expect(sumMemoized('1', '2')).to.equal('12');
expect(sumCalled).to.equal(1);
// Return from cache
expect(sumMemoized('1', '2')).to.equal('12');
expect(sumCalled).to.equal(1);
sumMemoized.clearCache();
// Now the original function is called again
expect(sumMemoized('1', '2')).to.equal('12');
expect(sumCalled).to.equal(3);
// Return from new cache again
expect(sumMemoized('1', '2')).to.equal('12');
expect(sumCalled).to.equal(3);
});
});
});