Last active
August 25, 2020 16:54
-
-
Save wangziling/739ebed0dcb25d7b967858baed7143e6 to your computer and use it in GitHub Desktop.
Made a function result cacheable, no matter its args' length.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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