feat(providence): allow to clear cache of memoized function
This commit is contained in:
parent
1df3854196
commit
994a7b5266
2 changed files with 55 additions and 23 deletions
|
|
@ -24,30 +24,33 @@ function createCachableArg(arg) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template T
|
* @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 = {} } = {}) {
|
export function memoize(functionToMemoize, { cacheStorage = {} } = {}) {
|
||||||
return /** @type {* & T} */ (
|
function memoizedFn() {
|
||||||
function memoizedFn() {
|
// eslint-disable-next-line prefer-rest-params
|
||||||
// eslint-disable-next-line prefer-rest-params
|
const args = [...arguments];
|
||||||
const args = [...arguments];
|
const shouldSerialize = args.some(isObject);
|
||||||
const shouldSerialize = args.some(isObject);
|
|
||||||
|
|
||||||
const cachableArgs = shouldSerialize ? args.map(createCachableArg) : args;
|
const cachableArgs = shouldSerialize ? args.map(createCachableArg) : args;
|
||||||
// Allow disabling of cache for testing purposes
|
// Allow disabling of cache for testing purposes
|
||||||
|
// @ts-expect-error
|
||||||
|
if (shouldCache && cachableArgs in cacheStorage) {
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
if (shouldCache && cachableArgs in storage) {
|
return cacheStorage[cachableArgs];
|
||||||
// @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;
|
|
||||||
}
|
}
|
||||||
);
|
// @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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -201,13 +201,13 @@ describe('Memoize', () => {
|
||||||
sumCalled += 1;
|
sumCalled += 1;
|
||||||
return { ...a, ...b };
|
return { ...a, ...b };
|
||||||
}
|
}
|
||||||
const sumMemoized = memoize(sum, { serializeObjects: true });
|
const sumMemoized = memoize(sum);
|
||||||
let sum2Called = 0;
|
let sum2Called = 0;
|
||||||
function sum2(/** @type {object} a */ a, /** @type {object} a */ b) {
|
function sum2(/** @type {object} a */ a, /** @type {object} a */ b) {
|
||||||
sum2Called += 1;
|
sum2Called += 1;
|
||||||
return { ...a, ...b };
|
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(sumMemoized({ x: 1 }, { y: 2 })).to.deep.equal({ x: 1, y: 2 });
|
||||||
expect(sumCalled).to.equal(1);
|
expect(sumCalled).to.equal(1);
|
||||||
|
|
@ -233,7 +233,7 @@ describe('Memoize', () => {
|
||||||
sumCalled += 1;
|
sumCalled += 1;
|
||||||
return { ...a, ...b };
|
return { ...a, ...b };
|
||||||
}
|
}
|
||||||
const sumMemoized = memoize(sum, { serializeObjects: true });
|
const sumMemoized = memoize(sum);
|
||||||
|
|
||||||
// Put in cache for args combination
|
// Put in cache for args combination
|
||||||
const result = sumMemoized({ x: 1 }, { y: 2 });
|
const result = sumMemoized({ x: 1 }, { y: 2 });
|
||||||
|
|
@ -313,4 +313,33 @@ describe('Memoize', () => {
|
||||||
expect(sum2Called).to.equal(1);
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue