Skip to content

Commit

Permalink
Fix caching bug with multiple instances
Browse files Browse the repository at this point in the history
  • Loading branch information
edwinm committed Jul 10, 2023
1 parent 74007f2 commit 459f835
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 27 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
/.idea
/test/test.js
coverage/
/demo/demo.js
2 changes: 1 addition & 1 deletion dist/memoize.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**!
@preserve memoize-decorator 1.6.0
@preserve memoize-decorator 1.7.0
@copyright 2023 Edwin Martin
@license MIT
*/
Expand Down
26 changes: 17 additions & 9 deletions dist/memoize.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/memoize.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "memoize-cache-decorator",
"version": "1.6.0",
"version": "1.7.0",
"description": "Cache the result of a method or getter for better performance. Supports timeout and clearing of the cache.",
"type": "module",
"license": "MIT",
Expand Down
34 changes: 23 additions & 11 deletions src/memoize.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
/**!
@preserve memoize-decorator 1.6.0
@preserve memoize-decorator 1.7.0
@copyright 2023 Edwin Martin
@license MIT
*/

import stringify from "json-stringify-safe";

const cacheMap = new Map();
const cacheMap = new Map<(...args: any) => any, Map<string, CacheObject>>();
const idPropertySymbol = Symbol();
let uniqueObjectId = 1;

export interface Config {
resolver?: (...args: any[]) => string | number;
Expand All @@ -27,37 +29,47 @@ export function memoize(config: Config = {}) {
const prop = propertyDescriptor.value ? "value" : "get";

const originalFunction = propertyDescriptor[prop];
const map: Map<string | number, CacheObject> = new Map();
const functionCacheMap = new Map<string, CacheObject>();

propertyDescriptor[prop] = function (
this: { [id: symbol]: number },
...args: any[]
) {
let objectId = this[idPropertySymbol];
if (!objectId) {
objectId = ++uniqueObjectId;
this[idPropertySymbol] = objectId;
}

propertyDescriptor[prop] = function (...args: any[]) {
const key = config.resolver
? config.resolver.apply(this, args)
: stringify(args);
const cacheKey = `${objectId}:${key}`;

if (map.has(key)) {
const { result, timeout } = map.get(key)!;
if (functionCacheMap.has(cacheKey)) {
const { result, timeout } = functionCacheMap.get(cacheKey)!;

Check warning on line 50 in src/memoize.ts

View workflow job for this annotation

GitHub Actions / Run units tests

Forbidden non-null assertion

Check warning on line 50 in src/memoize.ts

View workflow job for this annotation

GitHub Actions / Run units tests

Forbidden non-null assertion
if (!config.ttl || timeout > Date.now()) {
return result;
}
}
const newResult = originalFunction.apply(this, args);
map.set(key, {
functionCacheMap.set(cacheKey, {
result: newResult,
timeout: config.ttl ? Date.now() + config.ttl : Infinity,
});
return newResult;
};

cacheMap.set(propertyDescriptor[prop], map);
cacheMap.set(propertyDescriptor[prop], functionCacheMap);

return propertyDescriptor;
};
}

export function clear(fn: (...args: any) => any) {
const map = cacheMap.get(fn);
const functionCacheMap = cacheMap.get(fn);

if (map) {
map.clear();
if (functionCacheMap) {
functionCacheMap.clear();
}
}
17 changes: 15 additions & 2 deletions test/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ it("Test resolver", () => {
});

it("Test ttl", async () => {
const example = new Example();
expect(example.expiring40()).toEqual("a=10");
expect(example.expiring60()).toEqual("a=10");
example.a++;
Expand All @@ -123,8 +122,22 @@ it("Test ttl", async () => {
expect(example.expiring60()).toEqual("a=15");
});

it("Test ttl with two instances", async () => {
const example2 = new Example();
expect(example.expiring40()).toEqual("a=10");
example.a++;
example2.a++;
await new Promise((resolve) => setTimeout(resolve, 30));
expect(example.expiring40()).toEqual("a=10");
expect(example2.expiring40()).toEqual("a=11");
example.a++;
example2.a++;
await new Promise((resolve) => setTimeout(resolve, 20));
expect(example.expiring40()).toEqual("a=12");
expect(example2.expiring40()).toEqual("a=11");
});

it("Test ttl with args", async () => {
const example = new Example();
expect(example.expiringArg("a")).toEqual("arg=10-a");
example.a++;
await new Promise((resolve) => setTimeout(resolve, 20));
Expand Down

0 comments on commit 459f835

Please sign in to comment.