async getWares() {
let key = 'wares'
// 從data 緩存中獲取 數據
let data = dataCache.get(key)
if (!data) {
// 沒有數據請求服務器
const res = await request.get('/getWares')

// 其他操作
...
data = ...

// 設置數據緩存
dataCache.set(key, data)

}
return data
}

第一行代碼 使用了 es6以上的 Map,如果對map不是很理解的情況下,你可以參考
ECMAScript 6 入門 Set 和 Map 或者 Exploring ES6 關于 map 和 set的介紹,此處可以理解為一個鍵值對存儲結構。

之后 代碼 使用 了 async 函數,可以將異步操作變得更為方便。你可以參考ECMAScript 6 入門 async函數來進行學習或者鞏固知識。

代碼本身很容易理解,是利用 Map 對象對數據進行緩存,之后調用從 Map 對象來取數據。對于及其簡單的業務場景,直接利用此代碼即可。

調用方式:

getWares().then( ... )
// 第二次調用 取得先前的data
getWares().then( ... )

方案二、 promise緩存

方案一本身是不足的。因為如果考慮同時兩個以上的調用此 api,會因為請求未返回而進行第二次請求api。

當然,如果你在系統中添加類似于 vuex、redux這樣的單一數據源框架,這樣的問題不太會遇到,但是有時候我們想在各個復雜組件分別調用api,而不想對組件進行組件通信數據時候,便會遇到此場景。

const promiseCache = new Map()

getWares() {
const key = 'wares'
let promise = promiseCache.get(key);
// 當前promise緩存中沒有 該promise
if (!promise) {
promise = request.get('/getWares').then(res => {
// 對res 進行操作
...
}).catch(error => {
// 在請求回來后,如果出現問題,把promise從cache中刪除 以避免第二次請求繼續出錯S
promiseCache.delete(key)
return Promise.reject(error)
})
}
// 返回promise
return promise
}

該代碼避免了方案一的同一時間多次請求的問題。同時也在后端出錯的情況下對promise進行了刪除,不會出現緩存了錯誤的promise就一直出錯的問題。

調用方式:

getWares().then( ... )
// 第二次調用 取得先前的promise
getWares().then( ... )

方案三、 多promise 緩存

該方案是同時需要 一個以上 的api請求的情況下,對數據同時返回,如果某一個api發生錯誤的情況下。

均不返回正確數據。

const querys ={
wares: 'getWares',
skus: 'getSku'
}
const promiseCache = new Map()

async queryAll(queryApiName) {
// 判斷傳入的數據是否是數組
const queryIsArray = Array.isArray(queryApiName)
// 統一化處理數據,無論是字符串還是數組均視為數組
const apis = queryIsArray ? queryApiName : [queryApiName]

// 獲取所有的 請求服務
const promiseApi = []

apis.forEach(api => {
// 利用promise
let promise = promiseCache.get(api)

if (promise) {
// 如果 緩存中有,直接push
promise.push(promise)
} else {
promise = request.get(querys[api]).then(res => {
// 對res 進行操作
...
}).catch(error => {
// 在請求回來后,如果出現問題,把promise從cache中刪除
promiseCache.delete(api)
return Promise.reject(error)
})
promiseCache.set(api, promise)
promiseCache.push(promise)
}
})
return Promise.all(promiseApi).then(res => {
// 根據傳入的 是字符串還是數組來返回數據,因為本身都是數組操作
// 如果傳入的是字符串,則需要取出操作
return queryIsArray ? res : res[0]
})
}

該方案是同時獲取多個服務器數據的方式。可以同時獲得多個數據進行操作,不會因為單個數據出現問題而發生錯誤。

調用方式:

queryAll('wares').then( ... )
// 第二次調用 不會去取 wares,只會去skus
queryAll(['wares', 'skus']).then( ... )

方案四 、添加時間有關的緩存

往往緩存是有危害的,如果我們在知道修改了數據的情況下,直接把 cache 刪除即可,此時我們調用方法就可以向服務器進行請求。

這樣我們規避了前端顯示舊的的數據。但是我們可能一段時間沒有對數據進行操作,那么此時舊的數據就一直存在,那么我們最好規定個時間來去除數據。

該方案是采用了 類 持久化數據來做數據緩存,同時添加了過期時長數據以及參數化。 

代碼如下: 

首先定義持久化類,該類可以存儲 promise 或者 data

class ItemCache() {
construct(data, timeout) {
this.data = data
// 設定超時時間,設定為多少秒
this.timeout = timeout
// 創建對象時候的時間,大約設定為數據獲得的時間
this.cacheTime = (new Date()).getTime
}
}

然后我們定義該數據緩存。我們采用Map 基本相同的api

class ExpriesCache {
// 定義靜態數據map來作為緩存池
static cacheMap = new Map()

// 數據是否超時
static isOverTime(name) {
const data = ExpriesCache.cacheMap.get(name)

// 沒有數據 一定超時
if (!data) return true

// 獲取系統當前時間戳
const currentTime = (new Date()).getTime()

// 獲取當前時間與存儲時間的過去的秒數
const overTime = (currentTime - data.cacheTime) / 1000

// 如果過去的秒數大于當前的超時時間,也返回null讓其去服務端取數據
if (Math.abs(overTime) > data.timeout) {
// 此代碼可以沒有,不會出現問題,但是如果有此代碼,再次進入該方法就可以減少判斷。
ExpriesCache.cacheMap.delete(name)
return true
}

// 不超時
return false
}

// 當前data在 cache 中是否超時
static has(name) {
return !ExpriesCache.isOverTime(name)
}

// 刪除 cache 中的 data
static delete(name) {
return ExpriesCache.cacheMap.delete(name)
}

// 獲取
static get(name) {
const isDataOverTiem = ExpriesCache.isOverTime(name)
//如果 數據超時,返回null,但是沒有超時,返回數據,而不是 ItemCache 對象
return isDataOverTiem ? null : ExpriesCache.cacheMap.get(name).data
}

// 默認存儲20分鐘
static set(name, data, timeout = 1200) {
// 設置 itemCache
const itemCache = mew ItemCache(data, timeout)
//緩存
ExpriesCache.cacheMap.set(name, itemCache)
}
}

此時數據類以及操作類 都已經定義好,我們可以在api層這樣定義

// 生成key值錯誤
const generateKeyError = new Error("Can't generate key from name and argument")

// 生成key值
function generateKey(name, argument) {
// 從arguments 中取得數據然后變為數組
const params = Array.from(argument).join(',')

try{
// 返回 字符串,函數名 + 函數參數
return ${name}:${params} }catch(_) { // 返回生成key錯誤 return generateKeyError } } async getWare(params1, params2) { // 生成key const key = generateKey('getWare', [params1, params2]) // 獲得數據 let data = ExpriesCache.get(key) if (!data) { const res = await request('/getWares', {params1, params2}) // 使用 10s 緩存,10s之后再次get就會 獲取null 而從服務端繼續請求 ExpriesCache.set(key, res, 10) } return data }

該方案使用了 過期時間 和 api 參數不同而進行 緩存的方式。已經可以滿足絕大部分的業務場景。

調用方式:

getWares(1,2).then( ... )
// 第二次調用 取得先前的promise
getWares(1,2).then( ... )
// 不同的參數,不取先前promise
getWares(1,3).then( ... )

方案五、基于修飾器的方案四

和方案四是的解法一致的,但是是基于修飾器來做。
代碼如下:

// 生成key值錯誤
const generateKeyError = new Error("Can't generate key from name and argument")

// 生成key值
function generateKey(name, argument) {
// 從arguments 中取得數據然后變為數組
const params = Array.from(argument).join(',')
try{
// 返回 字符串
return ${name}:${params} }catch(_) { return generateKeyError } } function decorate(handleDescription, entryArgs) { // 判斷 當前 最后數據是否是descriptor,如果是descriptor,直接 使用 // 例如 log 這樣的修飾器 if (isDescriptor(entryArgs[entryArgs.length - 1])) { return handleDescription(...entryArgs, []) } else { // 如果不是 // 例如 add(1) plus(20) 這樣的修飾器 return function() { return handleDescription(...Array.protptype.slice.call(arguments), entryArgs) } } } function handleApiCache(target, name, descriptor, ...config) { // 拿到函數體并保存 const fn = descriptor.value // 修改函數體 descriptor.value = function () { const key = generateKey(name, arguments) // key無法生成,直接請求 服務端數據 if (key === generateKeyError) { // 利用剛才保存的函數體進行請求 return fn.apply(null, arguments) } let promise = ExpriesCache.get(key) if (!promise) { // 設定promise promise = fn.apply(null, arguments).catch(error => { // 在請求回來后,如果出現問題,把promise從cache中刪除 ExpriesCache.delete(key) // 返回錯誤 return Promise.reject(error) }) // 使用 10s 緩存,10s之后再次get就會 獲取null 而從服務端繼續請求 ExpriesCache.set(key, promise, config[0]) } return promise } return descriptor; } // 制定 修飾器 function ApiCache(...args) { return decorate(handleApiCache, args) }

此時 我們就會使用 類來對api進行緩存

class Api {
// 緩存10s
@ApiCache(10)
// 此時不要使用默認值,因為當前 修飾器 取不到
getWare(params1, params2) {
return request.get('/getWares')
}
}

因為函數存在函數提升,所以沒有辦法利用函數來做 修飾器?
例如:

var counter = 0;

var add = function () {
counter++;
};

@add
function foo() {
}

該代碼意圖是執行后counter等于 1,但是實際上結果是counter等于 0。因為函數提升,使得實際執行的代碼是下面這樣

@add
function foo() {
}

var counter;
var add;

counter = 0;

add = function () {
counter++;
};

所以沒有 辦法在函數上用修飾器。具體參考ECMAScript 6 入門 Decorator 
此方式寫法簡單且對業務層沒有太多影響。但是不可以動態修改 緩存時間

調用方式

getWares(1,2).then( ... )
// 第二次調用 取得先前的promise
getWares(1,2).then( ... )
// 不同的參數,不取先前promise
getWares(1,3).then( ... )

總結

api的緩存機制與場景在這里也基本上介紹了,基本上能夠完成絕大多數的數據業務緩存,在這里我也想請教教大家,有沒有什么更好的解決方案,或者這篇博客中有什么不對的地方,歡迎指正,在這里感謝各位了。?

同時這里也有很多沒有做完的工作,可能會在后面的博客中繼續完善。

文章轉自微信公眾號@前端技術精選

熱門推薦
一個賬號試用1000+ API
助力AI無縫鏈接物理世界 · 無需多次注冊
3000+提示詞助力AI大模型
和專業工程師共享工作效率翻倍的秘密
返回頂部
上一篇
接口優化的幾個技巧
下一篇
大規模分布式架構中,怎樣設計和選擇 API 限流技術?
国内精品久久久久影院日本,日本中文字幕视频,99久久精品99999久久,又粗又大又黄又硬又爽毛片
成人深夜在线观看| 中文字幕一区三区| 欧美酷刑日本凌虐凌虐| 国产欧美日产一区| 国产精品一区不卡| 久久久久一区二区三区四区| 久久国产精品色| 精品国产凹凸成av人网站| 秋霞午夜鲁丝一区二区老狼| 91精品国产91久久久久久最新毛片| 亚洲综合色区另类av| 欧美午夜精品久久久久久超碰| 亚洲欧美激情一区二区| 99久精品国产| 天堂午夜影视日韩欧美一区二区| 在线播放亚洲一区| 精品一区二区在线播放| 久久久精品一品道一区| 国产成人精品在线看| 中文字幕不卡在线观看| 欧美综合天天夜夜久久| 日本三级韩国三级欧美三级| 久久精品网站免费观看| 色婷婷综合久久久| 精品一区二区在线观看| 国产精品久久久久久久久久久免费看 | 亚洲精品视频免费看| 久久国产人妖系列| 久久人人超碰精品| 国产精品久久久久影院亚瑟| 日本高清不卡视频| 亚洲一区二区三区影院| 国产拍欧美日韩视频二区| 91蜜桃网址入口| 麻豆精品在线播放| 国产女人18毛片水真多成人如厕 | 中文字幕成人av| 欧美成人福利视频| 在线看国产日韩| 成人毛片视频在线观看| 日本美女视频一区二区| 亚洲精品高清在线| 国产精品高清亚洲| 精品福利一二区| 欧美成人性战久久| 欧美精品v日韩精品v韩国精品v| 99精品在线观看视频| 国产精品91一区二区| 久久国产精品一区二区| 日韩av一二三| 丝袜亚洲另类丝袜在线| 亚洲18影院在线观看| 一区二区视频在线| 一区二区三区在线视频观看 | eeuss鲁片一区二区三区在线看 | 处破女av一区二区| 国产成人亚洲综合a∨婷婷图片| 精品一区二区三区在线观看| 久草在线在线精品观看| 九色综合狠狠综合久久| 久久精品国产第一区二区三区| 麻豆精品视频在线观看视频| 三级亚洲高清视频| 激情文学综合丁香| 风间由美一区二区av101| caoporen国产精品视频| 色哦色哦哦色天天综合| 正在播放亚洲一区| 日韩欧美国产一区二区三区| 欧美精品一区男女天堂| 国产精品国产成人国产三级 | 91视视频在线观看入口直接观看www| 91亚洲精品一区二区乱码| 欧美日免费三级在线| 日韩精品一区二区三区在线| 国产精品免费视频观看| 亚洲一区二区av电影| 麻豆精品视频在线观看视频| 成人丝袜18视频在线观看| 91女人视频在线观看| 日韩免费看的电影| 国产精品初高中害羞小美女文| 亚洲动漫第一页| 国产suv精品一区二区883| 在线国产亚洲欧美| 国产日韩欧美a| 日韩精品五月天| 97精品国产露脸对白| 日韩精品一区二区三区中文精品| 亚洲精品欧美激情| 极品美女销魂一区二区三区| 91久久久免费一区二区| 久久久久久久久一| 日本不卡的三区四区五区| 91网址在线看| 亚洲国产精品ⅴa在线观看| 欧美a一区二区| 欧美日韩激情一区二区| 亚洲三级免费观看| 国产69精品久久久久777| 欧美激情在线观看视频免费| 国模冰冰炮一区二区| 不卡视频在线观看| 午夜精品久久久久久久久久久| 日韩免费一区二区| 91色porny在线视频| 欧美久久久久久蜜桃| 午夜一区二区三区视频| 宅男在线国产精品| 91黄视频在线| 欧美三级日本三级少妇99| 成人av电影免费在线播放| 成人av影视在线观看| 婷婷综合五月天| 国产精品传媒入口麻豆| 欧美综合欧美视频| 精品免费视频一区二区| 色一情一伦一子一伦一区| 亚洲视频网在线直播| 91麻豆精品国产91久久久久久| 国模冰冰炮一区二区| 1区2区3区国产精品| 色综合天天综合网国产成人综合天| 亚洲国产一区视频| 久久久精品影视| 欧美精品亚洲二区| 91在线视频观看| 国产一区久久久| 日韩视频免费观看高清完整版在线观看| 国产精品免费视频网站| 成人自拍视频在线观看| 欧美本精品男人aⅴ天堂| 日韩极品在线观看| 欧美mv和日韩mv国产网站| av在线综合网| 一本久道中文字幕精品亚洲嫩| 日韩一区二区三区四区| 日韩国产欧美在线观看| 久久一日本道色综合| voyeur盗摄精品| 亚洲精品高清在线| 67194成人在线观看| 国产成人午夜精品5599| 亚洲精品久久7777| 欧美电影免费观看完整版| 粉嫩av亚洲一区二区图片| 亚洲国产精品自拍| 久久免费看少妇高潮| 日本道在线观看一区二区| 激情久久五月天| 亚洲综合丁香婷婷六月香| 777欧美精品| 成人教育av在线| 免费高清视频精品| 中文字幕成人在线观看| 88在线观看91蜜桃国自产| 成人午夜免费av| 石原莉奈在线亚洲三区| 亚洲综合色丁香婷婷六月图片| 国产片一区二区三区| 日韩欧美在线1卡| 色狠狠色噜噜噜综合网| 成人免费精品视频| 国产麻豆日韩欧美久久| 日本午夜一区二区| 亚洲午夜影视影院在线观看| 中文字幕一区二区三区四区| 精品国产99国产精品| 欧美日韩国产影片| 一道本成人在线| 成人动漫av在线| 成人免费不卡视频| 国产成人综合精品三级| 国产另类ts人妖一区二区| 蓝色福利精品导航| 日韩高清在线观看| 亚洲男同性视频| 亚洲精品久久久蜜桃| 一区二区三区四区高清精品免费观看 | 精品制服美女久久| 蜜臀av国产精品久久久久| 婷婷久久综合九色综合绿巨人| 一区二区三区在线播放| 夜夜精品视频一区二区| 亚洲高清免费视频| 蜜桃视频一区二区三区在线观看| 日本成人在线一区| 麻豆视频一区二区| 国产一级精品在线| 丰满放荡岳乱妇91ww| 国产成人高清在线| 99久久免费精品| 欧美视频你懂的| 欧美大片在线观看一区二区| 精品国产91乱码一区二区三区 | 久久天天做天天爱综合色| 国产精品乱码妇女bbbb| 亚洲一区二区三区美女| 麻豆视频观看网址久久| 色综合久久综合中文综合网|