记忆函数的一些思考
前言
我在 React 中中处理 Promise 依赖执行时时长遇到这样的场景,A、B、C 三个函数,但是 B 和 C 依赖 A 函数执行的结果执行,并且 B 和 C 的执行很可能在两个完全不着边际的组件中,没有联系。同时 A 函数有可能存在大性能消耗或者大资源请求的请求的情况,那么重复的请求就浪费一定资源,也有可能会造成多次系统的等待挂起。
B、C 也许没有先后顺序,串行并行都有可能,那么从 B、C 或者更多依赖 A 函数计算的函数就不好一一去进行优化处理。最好的方式就是对 A 进行缓存。
这样的场景在开发时大多存在于请求的依赖或计算的依赖。
使用记忆函数进行处理
对这些重复且不那么依靠计算新鲜度的函数,我想到的方法就是缓存,函数计算的缓存那就是记忆函数。
它的实现很简单,这是一个简单的设计:
/**
* 记忆函数
*
* @param targetFunction 需要进行计算缓存的函数
* @param createMemoryID 获取记忆键的函数
* @returns 一个 targetFunction 的高阶记忆函数,附加 historyExec 对记忆进行控制
*/
export function memoryFunction<P extends Array<unknown>, R>(
targetFunction: (...p: P) => R,
createMemoryID: (args: P) => any = JSON.stringify
) {
const memoryMap = new Map();
function HOFTargetFunction(...p: P): R {
const MemoryID = createMemoryID(p);
if (memoryMap.has(MemoryID)) {
return memoryMap.get(MemoryID);
}
const result = targetFunction(...p);
memoryMap.set(MemoryID, result);
return result;
}
return Object.assign(HOFTargetFunction, { memoryMap });
}
上面函数中,我们对目标函数的执行结果进行保存,通过 createMemoryID
系列化函数参数生成这次执行的缓存标识,以记录缓存的位置。
缓存标识进行序列化是最好的,因为参数数组总是新的,没办法很好的通过引用值来进行缓存记录,除非固定参数数量。
这只是个简单的雏形,但是已经足够使用了。
简单使用
一个简单的例子:
// 用例函数
const foo = (index: number) => {
console.log(`index is ${index}`);
return index * 2;
};
// 用例函数的记忆函数
const fooMemo = memoryFunction(foo);
fooMemo(10); // 20
fooMemo(10); // 20
// 仅输出一次 "index is 10"
访问异步函数并清理缓存:
const foo = () => new Promise((resolve) => setTimeout(() => resolve(Math.random()), 500));
const fooMemo = memoryFunction(foo);
// 打印结果记作 R1
fooMemo().then(console.log);
setTimeout(() => {
// 此时多次打印结果和 R1 相同
fooMemo().then(console.log);
fooMemo().then(console.log);
// 清除记忆后再次执行,结果和 R1 不同
fooMemo.memoryMap.clear();
fooMemo().then(console.log);
}, 2000);
你不必担心清理缓存对旧执行的影响,因为没有缓存时总是返回当前执行的结果:
const foo = () => new Promise((resolve) => setTimeout(() => resolve(Math.random()), 500));
const fooMemo = memoryFunction(foo);
// 因为每次执行清理了缓存,所以每次的执行结果总是新鲜的
for (let i = 0; i < 10; i++) {
fooMemo().then(console.log);
fooMemo.memoryMap.clear();
}
// 打印结果类似:
// 0.554479766551786
// 0.5500302592982389
// 0.7122983594409036
// ...