詳解JS如何使用Promise緩存網(wǎng)絡(luò)請求
背景與概念
在進(jìn)行網(wǎng)絡(luò)請求的應(yīng)用中,尤其是數(shù)據(jù)變化不頻繁的場景下,緩存是一個常用而有效的優(yōu)化手段。緩存可以減少網(wǎng)絡(luò)延遲,避免不必要的數(shù)據(jù)傳輸,節(jié)省帶寬,提高用戶體驗(yàn)。但是,不當(dāng)?shù)木彺娌呗钥赡軐?dǎo)致用戶看到過時的數(shù)據(jù)。因此,實(shí)現(xiàn)一個合理的緩存策略是非常重要的。
Promise是JavaScript中處理異步操作的一種模式,它代表一個尚未完成但預(yù)計將來會完成的操作。通過Promise,可以將異步操作隊(duì)列化,鏈?zhǔn)教幚?,并在適當(dāng)?shù)臅r候進(jìn)行錯誤處理。
緩存網(wǎng)絡(luò)請求的需求與挑戰(zhàn)
在許多情況下,當(dāng)多個組件或者頁面需要相同的數(shù)據(jù)時,每個組件各自發(fā)起網(wǎng)絡(luò)請求顯得非常低效。這需要一種機(jī)制能夠“共享”已經(jīng)發(fā)起的請求,當(dāng)請求完成后,所有需要這份數(shù)據(jù)的組件都能獲得到。同時,若數(shù)據(jù)已經(jīng)在本地緩存,則無需重新發(fā)起網(wǎng)絡(luò)請求,直接使用緩存數(shù)據(jù)即可。
實(shí)現(xiàn)這樣的功能,有如下幾個挑戰(zhàn):
- 請求的復(fù)用性:避免同一數(shù)據(jù)的重復(fù)請求。
- 請求的并發(fā)管理:針對相同的數(shù)據(jù)來源同一時間只發(fā)送一次請求。
- 緩存的合理性:緩存有效數(shù)據(jù),但要避免過時數(shù)據(jù)的問題。
- 緩存的容錯性:當(dāng)請求失敗時,如何處理以及如何通知依賴這份數(shù)據(jù)的其他部分。
Promise緩存網(wǎng)絡(luò)請求的實(shí)現(xiàn)
具體到實(shí)現(xiàn),可以創(chuàng)建一個緩存對象,用于存儲已經(jīng)發(fā)起的請求的Promise對象。在請求發(fā)起前,先檢查緩存中是否已存在相應(yīng)的Promise;如果存在,直接返回該P(yáng)romise;如果不存在,則發(fā)起新的請求,并將請求的Promise存儲到緩存中。
以下是一個簡化的實(shí)現(xiàn)示例,逐一解釋核心代碼:
import utils from '@/utils'; export const reqThenCacheData = ( name: string ): Promise<void | Promise<utils.ICachedData>> => { // 嘗試獲取緩存數(shù)據(jù) const cache = utils.cacheForData.get(name); // 如果存在緩存,直接返回緩存中的Promise對象 if (cache) return cache; // 沒有緩存,構(gòu)造請求URL,發(fā)起網(wǎng)絡(luò)請求 const url = `http://*******?name=${name}`; const currentHandler = ( request.get({ url }) as unknown as Promise<Array<utils.ICachedListData>> ) .then((data: Array<utils.ICachedListData>) => { // 對返回的數(shù)據(jù)進(jìn)行處理,得到一個子請求列表 const subList: Array<string> = data.map((sub) => { if (sub.is_default === 1) return sub.subName; }); return subList.filter((item) => item); }) .then((subNames: Array<string>) => { // 根據(jù)第一次請求的結(jié)果,構(gòu)建第二次請求的Promise數(shù)組 const subRequestPromises: Array<Promise<utils.ICachedData>> = subNames.map((subName: string) => { const subUrl = `http://*******?subName=${subName}`; return request.get({ url: subUrl }) as unknown as Promise<utils.ICachedData>; }); // 使用Promise.all等待所有子請求完成 return Promise.all(subRequestPromises); }) .then((subData: Array<utils.ICachedData>) => { // 可以進(jìn)一步處理所有子請求的結(jié)果 // 示例僅返回其中一個子請求結(jié)果 return subData[0]; }) .catch((error) => { console.log('請求數(shù)據(jù)失?。?, error); // 錯誤處理:可以決定是否從緩存中移除錯誤的Promise utils.cacheForData.delete(name); // 根據(jù)需要,可以在這里拋出錯誤或返回一個默認(rèn)值 throw error; }); // 存入緩存,便于下次直接使用 utils.cacheForData.set(name, currentHandler); return currentHandler; };
緩存邏輯概述
上述代碼演示了基于Promise的網(wǎng)絡(luò)請求緩存機(jī)制。首先,通過調(diào)用cacheForData.get(name)
嘗試獲取緩存中已存在的請求Promise,如果找到,則直接返回,避免重復(fù)請求。
如果緩存中沒有找到請求Promise,那么將發(fā)起一個新的網(wǎng)絡(luò)請求。請求返回的Promise通過鏈?zhǔn)降?code>.then()方法進(jìn)行處理。這些.then()
方法負(fù)責(zé)對返回的數(shù)據(jù)進(jìn)行處理和轉(zhuǎn)化,使用者可以根據(jù)實(shí)際情況添加具體的數(shù)據(jù)處理邏輯。
最后,使用catch()
方法處理可能發(fā)生的錯誤,并且將當(dāng)前請求的Promise對象使用cacheForData.set(name, currentHandler)
方法存儲到緩存中,以便后續(xù)重用。
多階段請求處理
上述代碼展示了如何處理一個需要多次網(wǎng)絡(luò)請求的場景。系統(tǒng)首先檢查緩存中是否有目標(biāo)數(shù)據(jù)的Promise,在緩存未命中時,發(fā)起一個初始網(wǎng)絡(luò)請求。取得初始數(shù)據(jù)后,提取必要的信息組成新的請求數(shù)組。
利用Promise.all()
方法,可以并行處理多個網(wǎng)絡(luò)請求,這個方法返回一個新的Promise,它將在所有請求完成時解析。這一過程是Promise緩存實(shí)現(xiàn)的核心,它使得各個請求之間可以共享狀態(tài),并在全部請求都完成后統(tǒng)一返回結(jié)果。
這種緩存策略的一個關(guān)鍵優(yōu)勢在于它的復(fù)用性和并發(fā)管理能力。即使有多個請求同時要求相同的資源,由于緩存機(jī)制的存在,真實(shí)的網(wǎng)絡(luò)請求只會發(fā)生一次。每個后續(xù)嘗試訪問此數(shù)據(jù)的操作都會得到一個掛起的Promise,而非觸發(fā)新的網(wǎng)絡(luò)請求。
緩存及請求細(xì)節(jié)處理
在網(wǎng)絡(luò)請求Promise后串聯(lián)多個.then()
方法時,是在處理與轉(zhuǎn)換原始請求返回的數(shù)據(jù)。示例中,首先處理的是將返回的列表按照特定條件篩選,然后對篩選后的結(jié)果進(jìn)行進(jìn)一步的請求。這里的關(guān)鍵是Promise.all()
,它確保了同時處理多個異步操作。
處理器currentHandler
的最后結(jié)果是一個新的Promise,它代表了一系列依賴于原始請求并經(jīng)過一定處理的數(shù)據(jù)。這個Promise作為最終結(jié)果,將被緩存并返回給調(diào)用者。
緩存和請求的一個重要細(xì)節(jié)是錯誤處理。當(dāng)請求出現(xiàn)錯誤時,通常不應(yīng)該將錯誤的Promise存儲到緩存中,因?yàn)檫@可能導(dǎo)致后續(xù)所有對該數(shù)據(jù)的請求都會立即返回錯誤。相反,可以選擇在錯誤處理邏輯中移除緩存項(xiàng),或者采取其他的恢復(fù)機(jī)制。
緩存策略的考慮
實(shí)現(xiàn)緩存時,還需要考慮它的有效性和過時機(jī)制。只有當(dāng)數(shù)據(jù)不頻繁變化或者對即時性要求不高的情況下,緩存才是合理的。尤其是在一些數(shù)據(jù)更新周期較長的場景,例如一些配置數(shù)據(jù)、用戶信息等,使用緩存能帶來顯著的性能提升。
有時候,需要設(shè)立緩存過期機(jī)制,即數(shù)據(jù)在緩存中存儲一段時間后失效,之后的請求將強(qiáng)制發(fā)起新的網(wǎng)絡(luò)請求以獲取最新數(shù)據(jù)。緩存過期機(jī)制可以通過定時器或者請求次數(shù)等方式來實(shí)現(xiàn)。
結(jié)語
合理地使用Promise和緩存可以顯著提升應(yīng)用的性能,減輕服務(wù)器的負(fù)擔(dān),并提供更加流暢的用戶體驗(yàn)。通過在正確的地方引入緩存,可以有效地避免不必要的網(wǎng)絡(luò)請求,加快數(shù)據(jù)的加載速度。在此基礎(chǔ)上,合理設(shè)置緩存失效機(jī)制,能夠確保用戶始終獲取到最新的數(shù)據(jù),避免緩存導(dǎo)致的數(shù)據(jù)過時問題。
通過上述示例的實(shí)踐,開發(fā)者可以根據(jù)自身應(yīng)用的需要,定制適合自己業(yè)務(wù)場景的網(wǎng)絡(luò)請求緩存方案。這不僅可以提高程序性能,還可以增加程序的魯棒性,優(yōu)化用戶的交互體驗(yàn)。
以上就是詳解JS如何使用Promise緩存網(wǎng)絡(luò)請求的詳細(xì)內(nèi)容,更多關(guān)于JS Promise緩存網(wǎng)絡(luò)請求的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
javascript實(shí)現(xiàn)禁止復(fù)制網(wǎng)頁內(nèi)容
這篇文章主要介紹了javascript實(shí)現(xiàn)禁止復(fù)制網(wǎng)頁內(nèi)容,需要的朋友可以參考下2014-12-12JS中的hasOwnProperty()和isPrototypeOf()屬性實(shí)例詳解
hasOwnProperty()和isPrototypeOf()這兩個屬性都是Object.prototype所提供:Object.prototype.hasOwnProperty()和Object.prototype.isPropertyOf(),下面給大家介紹這兩個屬性的方法和使用,一起看下吧2016-08-08深入理解JavaScript中的尾調(diào)用(Tail Call)
尾調(diào)用(Tail Call)是函數(shù)式編程的一個重要概念,下面這篇文章主要給大家深入的介紹了關(guān)于JavaScript中尾調(diào)用的相關(guān)資料,文中介紹的非常詳細(xì),相信對大家具有一定的參考價值,有需要的朋友們下面來一起看看吧。2017-02-02原生js實(shí)現(xiàn)的觀察者和訂閱者模式簡單示例
這篇文章主要介紹了原生js實(shí)現(xiàn)的觀察者和訂閱者模式,結(jié)合簡單實(shí)例形式分析了js觀察者和訂閱者模式的相關(guān)原理與實(shí)現(xiàn)技巧,需要的朋友可以參考下2020-04-04JavaScript中使用參數(shù)個數(shù)實(shí)現(xiàn)重載功能
這篇文章主要介紹了JavaScript中使用參數(shù)個數(shù)實(shí)現(xiàn)重載功能,需要的朋友可以參考下2017-09-09深入理解javascript構(gòu)造函數(shù)和原型對象
對象,是javascript中非常重要的一個梗,是否能透徹的理解它直接關(guān)系到你對整個javascript體系的基礎(chǔ)理解,說白了,javascript就是一群對象在攪。。(嗶!)。2014-09-09