Skip to content

Instantly share code, notes, and snippets.

@wangziling
Last active August 25, 2020 16:54
Show Gist options
  • Save wangziling/739ebed0dcb25d7b967858baed7143e6 to your computer and use it in GitHub Desktop.
Save wangziling/739ebed0dcb25d7b967858baed7143e6 to your computer and use it in GitHub Desktop.
Made a function result cacheable, no matter its args' length.
export interface ICachedFnTreeOptions<This = any> {
context: This;
manipulable: boolean;
}
/**
* Made a function result cacheable, no matter its args' length.
* @param targetFn {(*) => *} The target function.
* @param [options] {ICachedFnTreeOptions} The options
* @return {(*) => *}
*/
export const cachedFnTree = (function () {
const treeMap: Map<Function, any> = new Map();
const maxTreeMapCount = 50;
const maxTreeResultMapLoopCount = 100;
const lowestResultSymbol = Symbol('totalResult');
const defaultOptions: ICachedFnTreeOptions = {
manipulable: false,
context: undefined // The `this`.
};
return function cachedFnTree<Result = any, This = any> (
targetFn: (...args: any[]) => Result | undefined,
options?: Partial<ICachedFnTreeOptions<This>>
) {
const config = {
...defaultOptions,
...(
options && typeof options === 'object'
? options
: {}
)
};
if (treeMap.size > maxTreeMapCount) {
treeMap.clear();
}
let resultMap: Map<any, any> = treeMap.get(targetFn);
if (!resultMap) {
treeMap.set(targetFn, resultMap = new Map());
}
// Record how many times we had looped.
let count = resultMap.size;
function cachedFnTreeMain (
this: This | undefined,
...args: Parameters<typeof targetFn>
): ReturnType<typeof targetFn> {
// If exceed, reset.
if (count > maxTreeResultMapLoopCount) {
count = 0;
resultMap.clear();
}
let tmpMap,
totalResult: Result | undefined = resultMap.get(lowestResultSymbol),
argsCount = args.length;
// No args.
if (!argsCount) {
if (!resultMap.has(lowestResultSymbol)) {
// Set cached result.
resultMap.set(
lowestResultSymbol,
totalResult = targetFn.apply(config.context || this, args)
);
}
// Return.
return totalResult;
}
args.reduce((curMap, arg, curIndex) => {
count++;
tmpMap = curMap.get(arg);
if (!tmpMap) {
curMap.set(
arg,
tmpMap = new Map() // Init.
);
}
// When got last arg, set or get result.
if (curIndex === (argsCount - 1)) {
if (tmpMap.has(lowestResultSymbol)) {
totalResult = tmpMap.get(lowestResultSymbol);
// Use the cached result.
return tmpMap;
}
// Set cached result.
tmpMap.set(
lowestResultSymbol,
totalResult = targetFn.apply(config.context || this, args)
);
}
return tmpMap;
}, resultMap);
// Return.
return totalResult;
}
// If we can operate manually.
if (config.manipulable) {
cachedFnTreeMain.clear = function clear () {
// If reset.
count = 0;
return resultMap.clear();
};
cachedFnTreeMain.removeValue = function removeValue (...args: Parameters<typeof targetFn> | any[]): boolean {
const len = args.length;
if (!len) return true;
const resultMap = cachedFnTreeMain.getItem(...args);
if (resultMap instanceof Map) {
return resultMap.delete(lowestResultSymbol);
}
return false;
};
cachedFnTreeMain.getItem = function getItem (...args: Parameters<typeof targetFn> | any[]): Map<any, any> | undefined {
return args.reduce(function (curMap, arg) {
if (!curMap) return curMap;
return curMap.get(arg);
}, resultMap);
};
cachedFnTreeMain.getValue = function getValue (...args: Parameters<typeof targetFn> | any[]): Result | undefined {
const lastMap = cachedFnTreeMain.getItem(...args);
return lastMap instanceof Map
? lastMap.get(lowestResultSymbol)
: undefined;
};
cachedFnTreeMain.setValue = function setValue (
args: Parameters<typeof targetFn> | any[],
result?: Result
): Result | undefined {
if (count > maxTreeResultMapLoopCount) {
cachedFnTreeMain.clear();
}
const len = args.length;
if (!len) return result;
let tmpMap;
args.reduce(function (curMap, arg, curIndex) {
count++;
tmpMap = curMap.get(arg);
if (!tmpMap) {
curMap.set(
arg,
tmpMap = new Map()
);
}
// Last.
if (curIndex === (len - 1)) {
tmpMap.set(lowestResultSymbol, result);
}
return tmpMap;
}, resultMap);
return result;
};
}
return cachedFnTreeMain;
};
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment