promise實(shí)現(xiàn)前端緩存
舉個(gè)常見的場(chǎng)景:在調(diào)用接口前都需要做一個(gè)權(quán)限check既鞠,偽代碼如下蔽豺。
function getDataFromServer(url, options){
return checkFromServer().then(()=>{
return axios(url,options);
});
}
function checkFromServer(){
return axios("/check").then((res)=>{
if(res.data.code===403){
throw new Error("permission deny!");
}
});
}
// 調(diào)用接口數(shù)據(jù)
getDataFromServer("/data",{});
上面的代碼看起來(lái)沒有什么問題蚪战。如果考慮并發(fā)狀態(tài)下呢链方?
getDataFromServer("/data",{});
getDataFromServer("/data1",{});
getDataFromServer("/data3",{});
在這里會(huì)觸發(fā)三次的/check請(qǐng)求此改,從實(shí)際情況出發(fā)的話,在短期內(nèi)可能只需要一次/check就可以了侄柔,性能上也能保障最優(yōu)共啃。
改造一下上面的代碼:
const checkPromise = null;
function getDataFromServer(url, options){
return checkFromServer().then(()=>{
return axios(url,options);
});
}
function checkFromServer(){
// 如果有緩存占调,則直接使用緩存
if(checkPromise){
return checkPromise;
}
checkPromise = axios("/check").then((res)=>{
if(res.data.code===403){
throw new Error("permission deny!");
}
// 5秒后清除緩存
setTimeout(()=>{
checkPromise = null;
},5000);
}).catch((err)=>{
checkPromise = null;
throw err;
});
return checkPromise;
}
// 調(diào)用接口數(shù)據(jù)
getDataFromServer("/data",{});
getDataFromServer("/data1",{});
getDataFromServer("/data3",{});
如上代碼,既解決了/check被調(diào)用多次的問題移剪,也解決了并發(fā)時(shí)候的問題究珊。看起來(lái)好像沒什么問題了纵苛。
再來(lái)考慮一個(gè)場(chǎng)景把剿涮,假設(shè)/check數(shù)據(jù)需要緩存5分鐘,5分鐘之內(nèi)都不會(huì)再去請(qǐng)求接口并考慮刷新頁(yè)面的情況呢攻人。
那就比較復(fù)雜了取试,數(shù)據(jù)可能需要落地到localstorage,可能還需要考慮緩存淘汰機(jī)制等情況怀吻。
這里有現(xiàn)成寫好的庫(kù)cache-in-stroage瞬浓。
快速應(yīng)用:
import { cacheDec } from "cache-in-storage";
const getTimeSpan = async (isError = false) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (isError) {
return reject(new Error("test"));
}
resolve(Date.now());
}, 200);
});
};
// 對(duì)getTimeSpan進(jìn)行緩存,并把緩存結(jié)果放入localStorage
const getTimeSpanWithCache = cacheDec(getTimeSpan, "keyInCache", { cache:true }, localStorage);
// 此時(shí)執(zhí)行方法返回的值都是相同的
getTimeSpanWithCache().then(console.log);
getTimeSpanWithCache().then(console.log);
getTimeSpanWithCache(true).catch(console.error);