From 994a7b526675720f06dfa4bd4bca89a6ce82ce38 Mon Sep 17 00:00:00 2001 From: Thijs Louisse Date: Fri, 17 May 2024 20:32:15 +0200 Subject: [PATCH] feat(providence): allow to clear cache of memoized function --- .../src/program/utils/memoize.js | 43 ++++++++++--------- .../test-node/program/utils/memoize.test.js | 35 +++++++++++++-- 2 files changed, 55 insertions(+), 23 deletions(-) diff --git a/packages-node/providence-analytics/src/program/utils/memoize.js b/packages-node/providence-analytics/src/program/utils/memoize.js index 64c7927be..7dd730b11 100644 --- a/packages-node/providence-analytics/src/program/utils/memoize.js +++ b/packages-node/providence-analytics/src/program/utils/memoize.js @@ -24,30 +24,33 @@ function createCachableArg(arg) { /** * @template T - * @type {(functionToMemoize:T, opts?:{ storage?:object; }) => T} + * @type {(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); } /** diff --git a/packages-node/providence-analytics/test-node/program/utils/memoize.test.js b/packages-node/providence-analytics/test-node/program/utils/memoize.test.js index 573726f3c..be40fb056 100644 --- a/packages-node/providence-analytics/test-node/program/utils/memoize.test.js +++ b/packages-node/providence-analytics/test-node/program/utils/memoize.test.js @@ -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); + }); + }); });