Skip to content

Commit

Permalink
feat(timed-memoize): additional memoize decorator with timeout
Browse files Browse the repository at this point in the history
  • Loading branch information
danrevah committed Feb 28, 2019
1 parent c343065 commit 6a40742
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 5 deletions.
12 changes: 7 additions & 5 deletions src/decorators/memoize/memoize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@ export function Memoize(hashFn?: (...args: any[]) => string) {
}

function decorator(fn: (...args: any[]) => any, hashFn?: (...args: any[]) => string) {
const cache: { [key: string]: any } = {};
const cache: { [key: string]: { value: string } } = {};

return function(...args: any[]) {
const hash = hashFn ? hashFn.apply(this, args) : JSON.stringify(args);
const key = hashFn ? hashFn.apply(this, args) : JSON.stringify(args);

if (!isUndefined(cache[hash])) {
return cache[hash];
if (!isUndefined(cache[key])) {
return cache[key].value;
}

return (cache[hash] = fn.apply(this, args));
cache[key] = { value: fn.apply(this, args) };

return cache[key].value;
};
}
22 changes: 22 additions & 0 deletions src/decorators/timed-memoize/timed-memoize.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { TimedMemoize } from './timed-memoize';

class TestTimedMemoize {
counter = 0;

@TimedMemoize(2000)
count(...args: any[]) {
return ++this.counter;
}
}

describe('TimedMemoize', () => {
it('should really invalidate cache',() => {
const past = Date.now() + 50000;
const test = new TestTimedMemoize();
expect(test.count(1)).toEqual(1);

jest.spyOn(Date, 'now').mockImplementation(() => past);

expect(test.count(1)).toEqual(2);
});
});
34 changes: 34 additions & 0 deletions src/decorators/timed-memoize/timed-memoize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { generateFunctionDecorator, isUndefined } from '../../helpers/general';

interface ITimedMemoizeProps {
hashFn?: (...args: any[]) => string,
invalidateOnTimeout?: boolean
}

export function TimedMemoize(timeoutMs: number, props: ITimedMemoizeProps = {}) {
return generateFunctionDecorator('TimedMemoize', decorator, timeoutMs, props);
}

function decorator<T>(fn: (...args: any[]) => any, timeoutMs: number, { hashFn, invalidateOnTimeout }: ITimedMemoizeProps) {
const cache: { [key: string]: { value: string, timeoutTimestamp: number } } = {};

return function(...args: any[]) {
const now = Date.now();
const key = hashFn ? hashFn.apply(this, args) : JSON.stringify(args);

if (!isUndefined(cache[key]) && cache[key].timeoutTimestamp > now) {
return cache[key].value;
}

cache[key] = {
value: fn.apply(this, args),
timeoutTimestamp: now + timeoutMs
};

if (invalidateOnTimeout) {
setTimeout(() => delete cache[key], timeoutMs);
}

return cache[key].value;
};
}

0 comments on commit 6a40742

Please sign in to comment.