计算优化
对于不变的结果能缓存就尽量缓存,这样子就减少每次使用的时候进行多次计算,浪费性能。
如:
- 同步结果:
利用 备忘模式 创建一个备忘函数 memorize;
将同步执行的函数fn进行备忘,这个函数fn的计算结果就会缓存起来,当你传入相同的参数的时候就会直接返回缓存的结果
function memorize (fn) {
const cache = {}
return function(){
const args = [].slice.call(arguments)
const key = JSON.stringify(args)
return cache[key] || (cache[key] = fn.apply(fn, args))
}
}
const add = (x, y) => x + y
const addFn = memorize(add)
const res = addFn(1,2) // 3, 第一次执行add方法得结果
const res = addFn(1,2) // 3 ,第二次从cache中拿结果
上面的addFn 就是经过备忘的函数,里面利用闭包将计算结果缓存起来,再次传入相同的参数调用时就会从cache中拿结果而不是再次计算。这样子的好处就是对于多次计算复杂的过程有很明显性能提升,缺点也很明显:参数一变,内存消耗就会增大,因为key是由参数决定的。这也是闭包的注意点,消耗内存不释放,那这里是否可以优化?当然可以,可以定义cache的一个最大上限,然后当存满时可用算法FIFO 或者 LRU释放。其实vue源码中也有用到这么一个函数,思想大体上一致,在vue/src/shared/util.js中
cached.png
- 异步结果
对于异步结果就不能直接用memorize函数进行备忘了,但是也可以变通改造一下
// 异步函数的备忘函数
function AsyncMemorize (AsyncFn) {
const cache = {}
return function(){
const args = [].slice.call(arguments)
const key = JSON.stringify(args)
return new Promise (function (resolve, reject) {
if (cache.hasOwnProperty(key)){
resolve(cache[key])
return
}
AsyncFn.apply(AsyncFn, args).then(res => {
cache[key] = res
resolve(res)
}).catch(e => reject(e))
})
}
}
实验一下,假如有个异步函数asyncAdd
const asyncAdd = (x, y) => {
return new Promise(resolve => {
setTimeout(() => resolve(x + y), 1000)
})
}
用法是:
asyncAdd(1,2).then(res => console.log(res)) // res : 3
那么对它进行备忘:
const asyncAdder = AsyncMemorize(asyncAdd)
asyncAdder(1,2).then(res1 => console.log(res1)) // 第一次等待1秒后输出res1: 3
asyncAdder(1,2).then(res1 => console.log(res1)) // 第二次不用等待1秒后就直接输出res1: 3
例子如下:
image.png
可以看到:
第一次执行asyncAdder(1,2).then(res1 => console.log(res1))的时候,
控制台先是打印Promise{<pending>} ,再打印结果3
第二次执行asyncAdder(1,2).then(res1 => console.log(res1))的时候,
控制台先是打印结果3 ,再打印Promise{<pending>}
实际应用场景:
前端需要通过ajax请求后端拿一个结果,这个结果是短时期或者长时期不会变的,那么就可以对这个结果进行备忘缓存,多次用到的时候只发一次请求,不用每次拿都发一次请求,起到节省带宽,减少用户等待时间、提升用户体验的作用。
总结
以上就是利用备忘模式分别对同步、异步结果进行缓存的使用方式,算是对计算性能的一个优化策略,值得参考。
网友评论