摘要:我們簡單測試下彳刂接下來我們要考慮一但緩存資源數超出了我們規定的時,我們要清理掉不常用的資源。是一個區塊管理框架,項目地址在這里區塊介紹在這里區塊介紹
轉載請注明出處:https://github.com/thx/magix/...
在前端開發過程中,我們經常會在內存中緩存一些數據,其實javascript的緩存比較簡單,只需要聲明一個變量或把一些數據掛到某個對象上即可,比如我們要實現一個對所有的ajax請求緩存的方法,簡單實現如下:
var cache={}; var request=function(url,callback){ if(cache[url]){ callback(cache[url]); }else{ $.ajax({ url:url, success:function(data){ callback(cache[url]=data); } }); } };
注意
示例中僅做簡單演示,未考慮同時對相同的url請求多次,比如
request("/a"); request("/a");
在上述代碼中仍然會發起2次對a的請求,這不是我們討論的重點,我們重點討論請求成功并緩存數據后,再請求該url的事情,所以這個問題略過不題
我們回頭看一下我們的request方法,會發現這樣的問題:
有些url在整個項目中或許只請求一次,我們仍然對它的結果進行緩存,造成資源被白白占用,如果應用在移動端,移動端的內存資源本身就比較寶貴,所以我們不能浪費
所以針對request方法中的緩存做一些改進,使它更智能些。我們需要一種算法,保證緩存的個數不能太多,同時緩存的資源數超多時,它能聰明的刪掉那些不常用的緩存數據
那我們看一下,當我們要實現這樣一個算法有哪些關鍵點要考慮:
我們需要知道緩存中緩存了多少個資源
當我們從緩存中獲取某個緩存資源時,獲取的算法復雜度應該是o(1),緩存模塊的作用是提高程序的效率,拿空間換時間,所以緩存模塊不應該占用過多的CPU時間
明確目標后,我們就需要尋找合適的對象來緩存我們的數據:
var obj={}
根據key從obj上查找某個對象,復雜度是o(1),滿足我們的第2條要求,但obj上緩存了多少個資源需要我們自已維護
var obj=[]
根據key查找某個對象時,復雜度是o(n),但數組有length,可以自動的幫我們維護當前緩存了多少個資源
我們知道數組是特殊的對象,所以我們可以把數組當成普通的對象來用。
當我們把一個緩存對象push進數組時,再根據緩存對象唯一的key,把它放到這個數組對象上
所以這時候我們第1版本的代碼可能類似這樣:
var Cache=function(){ this.$cache=[]; }; Cache.prototype.set=function(key,item){ var cache=this.$cache; var wrap={//包裝一次,方便我們放其它信息,同時利用對象引用傳遞 key:key, item:item }; cache.push(wrap); cache["cache_"+key]=wrap;//加上cache_的原因是:防止key是數字或可轉化為數字的字符串,這樣的話就變成了如 cache["2"] 通過下標訪問數組里面的元素了。 }; Cache.prototype.get=function(key){ var res=this.$cache["cache_"+key]; return res.item;//返回放入的資源 };
使用示例如下:
var c=new Cache(); c.set("/api/userinfo",{ name:"彳刂" }); console.log(c.get("/api/userinfo"));
這時候我們就完成了初步要求,知道緩存個數,查找時復雜度是o(1)
不過我們仍然需要更智能一些的緩存:
知道單個緩存資源的使用頻率
知道單個緩存資源的最后使用時間
緩存中最多能放多少個緩存資源
何時清理緩存資源
我們改造下剛才的代碼:
var Cache=function(max){ this.$cache=[]; + this.$max=max | 0 ||20; }; Cache.prototype.set=function(key,item){ var cache=this.$cache; - var wrap={//包裝一次,方便我們放其它信息,同時利用對象引用傳遞 - key:key, - item:item - }; + key="cache_"+key; + var wrap=cache[key]; + if(!cache.hasOwnProperty(key){ + wrap={}; + cache.push(wrap); + cache[key]=wrap; + } + wrap.item=item; + wrap.fre=1;//初始使用頻率為1 + wrap.key=key; + wrap.time=new Date().getTime(); }; Cache.prototype.get=function(key){ var res=this.$cache["cache_"+key]; if(res){ res.fre++;//更新使用頻率 res.time=new Date().getTime(); } return res.item;//返回放入的資源 };
在我們第2版本的代碼中,我們添加了最多緩存資源數max,同時每個緩存資源加入了使用頻率fre及最后使用時間time,同時我們修改了set方法,考慮了相同key的多次set問題。
我們簡單測試下:
var c=new Cache(); c.set("/api/userinfo",{ name:"彳刂" }); console.log(c.$cache[0].fre);//1 console.log(c.get("/api/userinfo")); console.log(c.$cache[0].fre);//2
接下來我們要考慮一但緩存資源數超出了我們規定的max時,我們要清理掉不常用的資源。清理時我們根據頻率的使用fre標志,fre最小的優先清理,同時相同的fre,我們優先清理time比較小的,這也是time設計的意義所在。
所以第3版我們的代碼如下:
var Cache=function(max){ this.$cache=[]; this.$max=max | 0 ||20; }; Cache.prototype.set=function(key,item){ var cache=this.$cache; key="cache_"+key; var wrap=cache[key]; if(!cache.hasOwnProperty(key){ + if(cache.length>=this.$max){ + cache.sort(function(a,b){ + return b.fre==a.fre?b.time-a.time:b.fre-a.fre; + }); + var item=cache.pop();//刪除頻率使用最小,時間最早的1個 + delete cache[item.key];// + } wrap={}; cache.push(wrap); cache[key]=wrap; } wrap.item=item; wrap.fre=1;//初始使用頻率為1 wrap.key=key; wrap.time=new Date().getTime(); }; Cache.prototype.get=function(key){ var res=this.$cache["cache_"+key]; if(res){ res.fre++;//更新使用頻率 res.time=new Date().getTime(); } return res.item;//返回放入的資源 }; +Cache.prototype.has=funciton(key){ + return this.$cache.hasOwnProperty("cache_"+key); +};
OK,到這里我們就完成了想要的緩存,我們結合最開始的request方法來進行實際測試:
var cache=new Cache(2); var request=function(url,callback){ if(cache.has(url)){ callback(cache.get(url); }else{ $.ajax({ url:url, success:function(data){ cache.set(url,data); callback(data); } }); } }) }; //實際使用(假設下一個request方法被調用時,前面request的已經完成請求并緩存好了數據): request("/api/item1"); request("/api/item2"); request("/api/item1");//命中緩存 request("/api/item3");//達到上限2,cache對象的內部$cache排序一次,刪除/api/item2的緩存 request("/api/item4");//仍然達到上限2,cache對象的內部$cache排序一次,刪除/api/item3的緩存 request("/api/item3");//接下來需要多次使用/api/item3,但在請求/api/item4時,它已經被刪除了,所以我們需要重新請求。完成請求后,因為上限2依然滿足,所以cache對象內部的$cache仍然需要排序一次,刪除/api/item4 request("/api/item3");//命中緩存
根據上述使用,我們發現,一但達到緩存的上限后,帶來的問題如下:
新的緩存資源進來一個,就需要重新排序一次,性能不好
有可能誤刪除接下來可能頻率使用到的緩存資源
這時候我們就需要尋找突破。類比我們經常使用的操作系統的緩存區,我們的緩存是否也可以加入一個緩沖區呢?當整個緩存列表加上緩沖區都滿的時候,才清空一次緩存區,不但能解決頻繁排序的問題,也能很好的保留接下來程序中可能頻繁使用到的緩存資源
來,緩存的第4版:
var Cache=function(max,buffer){ this.$cache=[]; this.$max=max | 0 ||20; + this.$buffer=buffer | 0 ||5; }; Cache.prototype.set=function(key,item){ var cache=this.$cache; key="cache_"+key; var wrap=cache[key]; if(!cache.hasOwnProperty(key){ - if(cache.length>=this.$max){ + if(cache.length>=this.$max+this.$buffer){ cache.sort(function(a,b){ return b.fre==a.fre?b.time-a.time:b.fre-a.fre; }); - var item=cache.pop();//刪除頻率使用最小,時間最早的1個 - delete cache[item.key];// + var buffer=this.$buffer; + while(buffer--){ + var item=cache.pop(); + delete cache[item.key]; + } } wrap={}; cache.push(wrap); cache[key]=wrap; } wrap.item=item; wrap.fre=1;//初始使用頻率為1 wrap.key=key; wrap.time=new Date().getTime(); }; Cache.prototype.get=function(key){ var res=this.$cache["cache_"+key]; if(res){ res.fre++;//更新使用頻率 res.time=new Date().getTime(); } return res.item;//返回放入的資源 }; Cache.prototype.has=funciton(key){ return this.$cache.hasOwnProperty("cache_"+key); };
這時候我們再結合request來測試一下:
var cache=new Cache(2,2);//最大2個,2個緩存區,真實可以緩存4個 var request=function(url,callback){ if(cache.has(url)){ callback(cache.get(url); }else{ //$.ajax略 } }; request("/api/item1"); request("/api/item2"); request("/api/item3");//放在緩沖區 request("/api/item4");//放在緩沖區 request("/api/item5");//排序一次,清除/api/item2 /api/item1 request("/api/item6");//放在緩沖區 request("/api/item7");//放在緩沖區
至此我們就完成了比較完善的緩存模塊
當然,后續我們增加緩存資源的生命期,比如20分鐘后清除,也是較容易的,不在這里詳解。
Magix的Cache模塊比這里稍微再復雜些,不過原理都是一樣的。
Magix是一個區塊管理框架,項目地址在這里magix
區塊介紹在這里magix區塊介紹
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/111414.html
摘要:我們簡單測試下彳刂接下來我們要考慮一但緩存資源數超出了我們規定的時,我們要清理掉不常用的資源。是一個區塊管理框架,項目地址在這里區塊介紹在這里區塊介紹 轉載請注明出處:https://github.com/thx/magix/... 在前端開發過程中,我們經常會在內存中緩存一些數據,其實javascript的緩存比較簡單,只需要聲明一個變量或把一些數據掛到某個對象上即可,比如我們要實現...
摘要:我們簡單測試下彳刂接下來我們要考慮一但緩存資源數超出了我們規定的時,我們要清理掉不常用的資源。是一個區塊管理框架,項目地址在這里區塊介紹在這里區塊介紹 轉載請注明出處:https://github.com/thx/magix/... 在前端開發過程中,我們經常會在內存中緩存一些數據,其實javascript的緩存比較簡單,只需要聲明一個變量或把一些數據掛到某個對象上即可,比如我們要實現...
摘要:我們簡單測試下彳刂接下來我們要考慮一但緩存資源數超出了我們規定的時,我們要清理掉不常用的資源。是一個區塊管理框架,項目地址在這里區塊介紹在這里區塊介紹 轉載請注明出處:https://github.com/thx/magix/... 在前端開發過程中,我們經常會在內存中緩存一些數據,其實javascript的緩存比較簡單,只需要聲明一個變量或把一些數據掛到某個對象上即可,比如我們要實現...
摘要:只要保證在開發中沒有對共享對象的寫入操作,那么發布到線上時肯定也沒有寫入操作,這時候這個保護方法就是多余的。 什么是共享對象 被多次使用到的同一個對象即為共享對象 比如我們用標準的es模塊來寫一個導出單位轉換的模塊 //converter module export default { cmToIn(){ //convert logic } } 當我們在...
摘要:面試題來源于網絡,看一下高級前端的面試題,可以知道自己和高級前端的差距。 面試題來源于網絡,看一下高級前端的面試題,可以知道自己和高級前端的差距。有些面試題會重復。 使用過的koa2中間件 koa-body原理 介紹自己寫過的中間件 有沒有涉及到Cluster 介紹pm2 master掛了的話pm2怎么處理 如何和MySQL進行通信 React聲明周期及自己的理解 如何...
閱讀 2665·2021-11-11 16:54
閱讀 3657·2021-08-16 10:46
閱讀 3441·2019-08-30 14:18
閱讀 3034·2019-08-30 14:01
閱讀 2723·2019-08-29 14:15
閱讀 2007·2019-08-29 11:31
閱讀 3083·2019-08-29 11:05
閱讀 2583·2019-08-26 11:54