js性能優(yōu)化-利用備忘模式進(jìn)行結(jié)果緩存

計(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中


cached.png
  • 異步結(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

例子如下:


image.png

可以看到:
第一次執(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)化策略,值得參考。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容