通過(guò)共享Promise解決前端重復(fù)請(qǐng)求的代碼示例
一、問(wèn)題場(chǎng)景分析
當(dāng)出現(xiàn)以下情況時(shí),可能導(dǎo)致重復(fù)請(qǐng)求:
- 用戶頻繁點(diǎn)擊觸發(fā)按鈕事件
- 組件快速重復(fù)掛載/更新
- 輸入框?qū)崟r(shí)搜索請(qǐng)求(如防抖失效)
- 多個(gè)獨(dú)立組件同時(shí)加載相同數(shù)據(jù)
二、核心實(shí)現(xiàn)思路
- 創(chuàng)建請(qǐng)求緩存池:存儲(chǔ)正在進(jìn)行的請(qǐng)求
- 請(qǐng)求唯一標(biāo)識(shí):通過(guò)參數(shù)生成請(qǐng)求唯一鍵
- Promise 復(fù)用:相同請(qǐng)求返回緩存中的 Promise
- 緩存清理機(jī)制:請(qǐng)求完成后自動(dòng)清理緩存
三、完整實(shí)現(xiàn)方案
1. 基礎(chǔ)版實(shí)現(xiàn)(ES6+)
// 請(qǐng)求緩存池 const requestCache = new Map(); function generateRequestKey(config) { return `${config.method}-${config.url}-${JSON.stringify(config.params)}`; } async function sharedRequest(config) { const requestKey = generateRequestKey(config); // 存在進(jìn)行中的相同請(qǐng)求 if (requestCache.has(requestKey)) { return requestCache.get(requestKey); } // 創(chuàng)建新請(qǐng)求并緩存 const requestPromise = axios(config) .then(response => { requestCache.delete(requestKey); // 成功清除緩存 return response; }) .catch(error => { requestCache.delete(requestKey); // 失敗也清除緩存 throw error; }); requestCache.set(requestKey, requestPromise); return requestPromise; }
2. 高級(jí)功能增強(qiáng)版
class RequestPool { constructor() { this.pool = new Map(); this.defaultTTL = 5000; // 默認(rèn)緩存5秒 } getKey(config) { const { method, url, params, data } = config; return `${method}-${url}-${JSON.stringify(params)}-${JSON.stringify(data)}`; } async request(config) { const key = this.getKey(config); const now = Date.now(); // 存在未過(guò)期的緩存 if (this.pool.has(key)) { const { expire, promise } = this.pool.get(key); if (expire > now) return promise; } // 創(chuàng)建新請(qǐng)求 const promise = axios(config).finally(() => { // 自動(dòng)清理或保留緩存 if (!config.keepAlive) { this.pool.delete(key); } }); // 緩存帶有效期 this.pool.set(key, { promise, expire: Date.now() + (config.cacheTTL || this.defaultTTL) }); return promise; } // 手動(dòng)清除緩存 clearCache(key) { this.pool.delete(key); } } // 使用示例 const apiPool = new RequestPool(); function fetchUserData(userId) { return apiPool.request({ method: 'GET', url: '/api/user', params: { id: userId }, cacheTTL: 10000 // 自定義緩存時(shí)間 }); }
四、關(guān)鍵點(diǎn)解析
1. 請(qǐng)求唯一標(biāo)識(shí)設(shè)計(jì)
組合關(guān)鍵參數(shù):method + url + 序列化后的params/data
序列化優(yōu)化:
function stableStringify(obj) { const keys = Object.keys(obj).sort(); return JSON.stringify(keys.map(k => ({ [k]: obj[k] }))); }
2. 緩存清理策略
策略類型 | 實(shí)現(xiàn)方式 | 適用場(chǎng)景 |
---|---|---|
即時(shí)清理 | 請(qǐng)求完成后立即刪除 | 常規(guī)數(shù)據(jù)請(qǐng)求 |
TTL 過(guò)期 | 檢查expire字段 | 需要短期緩存的數(shù)據(jù) |
手動(dòng)清理 | 提供clearCache方法 | 明確知道數(shù)據(jù)變更時(shí) |
LRU 算法 | 維護(hù)使用記錄+最大數(shù)量限制 | 高頻請(qǐng)求且內(nèi)存敏感場(chǎng)景 |
3. 錯(cuò)誤處理要點(diǎn)
.catch(error => { // 特殊錯(cuò)誤處理:網(wǎng)絡(luò)錯(cuò)誤可保留短暫緩存 if (error.isNetworkError) { setTimeout(() => this.pool.delete(key), 1000); } throw error; });
五、適用場(chǎng)景對(duì)比
方案 | 優(yōu)點(diǎn) | 缺點(diǎn) | 最佳使用場(chǎng)景 |
---|---|---|---|
基礎(chǔ)版 | 實(shí)現(xiàn)簡(jiǎn)單、內(nèi)存占用少 | 缺乏高級(jí)控制 | 簡(jiǎn)單頁(yè)面、少量API |
類封裝版 | 功能完善、擴(kuò)展性強(qiáng) | 實(shí)現(xiàn)復(fù)雜度較高 | 中大型項(xiàng)目、復(fù)雜場(chǎng)景 |
第三方庫(kù)(swr) | 開(kāi)箱即用、功能豐富 | 需要學(xué)習(xí)新API | 需要快速實(shí)現(xiàn)的復(fù)雜緩存需求 |
六、延伸優(yōu)化方向
- 請(qǐng)求競(jìng)速處理:
let abortController; function smartRequest() { if (abortController) { abortController.abort(); } abortController = new AbortController(); return fetch(url, { signal: abortController.signal }); }
- 本地緩存融合:
const response = await request(); if (response.ok) { localStorage.setItem(cacheKey, { data: response.data, expire: Date.now() + 3600000 }); }
- 可視化監(jiān)控:
// 在RequestPool類中添加 getCacheStatus() { return Array.from(this.pool.entries()).map(([key, item]) => ({ key, expireIn: item.expire - Date.now(), status: item.promise.isPending ? 'pending' : 'settled' })); }
通過(guò)這種實(shí)現(xiàn)方式,可以有效解決以下問(wèn)題:
- 減少 50%-90% 的重復(fù)網(wǎng)絡(luò)請(qǐng)求
- 避免組件重復(fù)渲染造成的性能損耗
- 保證多個(gè)組件間的數(shù)據(jù)一致性
- 降低服務(wù)端并發(fā)壓力
實(shí)際項(xiàng)目中可根據(jù)具體需求選擇基礎(chǔ)版或增強(qiáng)版實(shí)現(xiàn),建議配合 TypeScript 進(jìn)行類型約束以保證代碼健壯性。
以上就是通過(guò)共享Promise解決前端重復(fù)請(qǐng)求的代碼示例的詳細(xì)內(nèi)容,更多關(guān)于共享Promise解決重復(fù)請(qǐng)求的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaScript聲明變量的這四兄弟(var、let、function、const)
這篇文章主要介紹了JavaScript聲明變量的這四兄弟,主要就是介紹var、let、function、const區(qū)別,需要的朋友可以參考下2023-02-02利用d3.js力導(dǎo)布局繪制資源拓?fù)鋱D實(shí)例教程
這篇文章主要給大家介紹了關(guān)于如何利用d3.js力導(dǎo)布局繪制資源拓?fù)鋱D的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-01-01js使用DOM操作實(shí)現(xiàn)簡(jiǎn)單留言板的方法
這篇文章主要介紹了js使用DOM操作實(shí)現(xiàn)簡(jiǎn)單留言板的方法,涉及javascript中DOM操作的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-04-04js 剪切板的用法(clipboardData.setData)與js match函數(shù)介紹
這篇文章主要是對(duì)js中剪切板的使用方法(clipboardData.setData)與js中的match函數(shù)進(jìn)行了介紹。需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-11-11解析Javascript中難以理解的11個(gè)問(wèn)題
這篇文章主要是對(duì)Javascript中難以理解的11個(gè)問(wèn)題進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-12-12JS動(dòng)態(tài)調(diào)用方法名示例介紹
在JS中如何動(dòng)態(tài)調(diào)用方法名,想必很多的朋友們都不會(huì)吧,下面為大家舉例介紹下具體的調(diào)用方法2013-12-12讓低版本瀏覽器支持input的placeholder屬性(js方法)
低版本瀏覽器一般都不會(huì)支持input的placeholder屬性,接下來(lái)使用js實(shí)現(xiàn)下,感興趣的朋友可以參考下哈2013-04-04