projects/wms-framework/src/lib/decorators/Memoize.ts
Structure of values being memoized
Properties |
|
expirationDate |
expirationDate:
|
Type : number
|
Optional |
Expiration date (in milliseconds) of the value if defined |
value |
value:
|
Type : any
|
Value being memoized |
interface MemoizeArgs {
/**
* To indicate the value stored can be expired after a number of milliseconds
*
* @type {number}
* @memberof MemoizeArgs
*/
expireAfter?: number;
/**
* Hash function to use to generate the hashkey using the parameters
*
* @memberof MemoizeArgs
*/
hashFunction?: (...args: any[]) => any;
/**
* Callback function to execute before returning the result
*
* @memberof MemoizeArgs
*/
returnCallback?: (result: any) => any;
}
/**
* Structure of values being memoized
*
* @interface MemoizedEntry
*/
interface MemoizedEntry {
/**
* Value being memoized
*
* @type {*}
* @memberof MemoizedEntry
*/
value: any;
/**
* Expiration date (in milliseconds) of the value if defined
*
* @type {number}
* @memberof MemoizedEntry
*/
expirationDate?: number;
}
/**
* Decorator to use to do memoize of functions
*
* @export
* @param {MemoizeArgs} [args]
* @return {*}
*/
export function Memoize(args?: MemoizeArgs) {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
if (descriptor.value != null) {
descriptor.value = configureMemoize(descriptor.value, args);
} else {
throw 'Memoize decorator can be used only on methods';
}
};
}
function configureMemoize(
originalMethod: Function,
memoizeArgs?: MemoizeArgs
): any {
const undefinedKey = `__undefinedKey__${Date.now()}__`;
const nullKey = `__nullKey__${Date.now()}__`;
let buffer: Map<any, any> = new Map<any, any>();
return function (...args) {
const key =
memoizeArgs?.hashFunction?.apply?.(this, args) ??
(args.length > 0
? args
.map(
(a) =>
(a === undefined ? undefinedKey : a === null ? nullKey : a) + ''
)
.join('-')
: originalMethod);
let memoizedEntry: MemoizedEntry = buffer.get(key);
if (
memoizedEntry != null &&
(memoizedEntry.expirationDate == null ||
memoizedEntry.expirationDate > Date.now())
) {
return (
memoizeArgs?.returnCallback?.(memoizedEntry.value) ??
memoizedEntry.value
);
}
memoizedEntry = { value: originalMethod.apply(this, args) };
if (memoizeArgs?.expireAfter > 0)
memoizedEntry.expirationDate = Date.now() + memoizeArgs?.expireAfter;
buffer.set(key, memoizedEntry);
return (
memoizeArgs?.returnCallback?.(memoizedEntry.value) ?? memoizedEntry.value
);
};
}