計(jì)算優(yōu)化
對(duì)于不變的結(jié)果能緩存就盡量緩存,這樣子就減少每次使用的時(shí)候進(jìn)行多次計(jì)算,浪費(fèi)性能。
如:
- 同步結(jié)果:
利用 備忘模式 創(chuàng)建一個(gè)備忘函數(shù) memorize;
將同步執(zhí)行的函數(shù)fn進(jìn)行備忘,這個(gè)函數(shù)fn的計(jì)算結(jié)果就會(huì)緩存起來(lái),當(dāng)你傳入相同的參數(shù)的時(shí)候就會(huì)直接返回緩存的結(jié)果
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, 第一次執(zhí)行add方法得結(jié)果
const res = addFn(1,2) // 3 ,第二次從cache中拿結(jié)果
上面的addFn 就是經(jīng)過(guò)備忘的函數(shù),里面利用閉包將計(jì)算結(jié)果緩存起來(lái),再次傳入相同的參數(shù)調(diào)用時(shí)就會(huì)從cache中拿結(jié)果而不是再次計(jì)算。這樣子的好處就是對(duì)于多次計(jì)算復(fù)雜的過(guò)程有很明顯性能提升,缺點(diǎn)也很明顯:參數(shù)一變,內(nèi)存消耗就會(huì)增大,因?yàn)閗ey是由參數(shù)決定的。這也是閉包的注意點(diǎn),消耗內(nèi)存不釋放,那這里是否可以優(yōu)化?當(dāng)然可以,可以定義cache的一個(gè)最大上限,然后當(dāng)存滿時(shí)可用算法FIFO 或者 LRU釋放。其實(shí)vue源碼中也有用到這么一個(gè)函數(shù),思想大體上一致,在vue/src/shared/util.js中

- 異步結(jié)果
對(duì)于異步結(jié)果就不能直接用memorize函數(shù)進(jìn)行備忘了,但是也可以變通改造一下
// 異步函數(shù)的備忘函數(shù)
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))
})
}
}
實(shí)驗(yàn)一下,假如有個(gè)異步函數(shù)asyncAdd
const asyncAdd = (x, y) => {
return new Promise(resolve => {
setTimeout(() => resolve(x + y), 1000)
})
}
用法是:
asyncAdd(1,2).then(res => console.log(res)) // res : 3
那么對(duì)它進(jìn)行備忘:
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
例子如下:

可以看到:
第一次執(zhí)行asyncAdder(1,2).then(res1 => console.log(res1))的時(shí)候,
控制臺(tái)先是打印Promise{<pending>} ,再打印結(jié)果3
第二次執(zhí)行asyncAdder(1,2).then(res1 => console.log(res1))的時(shí)候,
控制臺(tái)先是打印結(jié)果3 ,再打印Promise{<pending>}
實(shí)際應(yīng)用場(chǎng)景:
前端需要通過(guò)ajax請(qǐng)求后端拿一個(gè)結(jié)果,這個(gè)結(jié)果是短時(shí)期或者長(zhǎng)時(shí)期不會(huì)變的,那么就可以對(duì)這個(gè)結(jié)果進(jìn)行備忘緩存,多次用到的時(shí)候只發(fā)一次請(qǐng)求,不用每次拿都發(fā)一次請(qǐng)求,起到節(jié)省帶寬,減少用戶等待時(shí)間、提升用戶體驗(yàn)的作用。
總結(jié)
以上就是利用備忘模式分別對(duì)同步、異步結(jié)果進(jìn)行緩存的使用方式,算是對(duì)計(jì)算性能的一個(gè)優(yōu)化策略,值得參考。