通過共享Promise解決前端重復(fù)請求的代碼示例
一、問題場景分析
當(dāng)出現(xiàn)以下情況時,可能導(dǎo)致重復(fù)請求:
- 用戶頻繁點(diǎn)擊觸發(fā)按鈕事件
- 組件快速重復(fù)掛載/更新
- 輸入框?qū)崟r搜索請求(如防抖失效)
- 多個獨(dú)立組件同時加載相同數(shù)據(jù)
二、核心實(shí)現(xiàn)思路
- 創(chuàng)建請求緩存池:存儲正在進(jìn)行的請求
- 請求唯一標(biāo)識:通過參數(shù)生成請求唯一鍵
- Promise 復(fù)用:相同請求返回緩存中的 Promise
- 緩存清理機(jī)制:請求完成后自動清理緩存
三、完整實(shí)現(xiàn)方案
1. 基礎(chǔ)版實(shí)現(xiàn)(ES6+)
// 請求緩存池
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)行中的相同請求
if (requestCache.has(requestKey)) {
return requestCache.get(requestKey);
}
// 創(chuà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. 高級功能增強(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();
// 存在未過期的緩存
if (this.pool.has(key)) {
const { expire, promise } = this.pool.get(key);
if (expire > now) return promise;
}
// 創(chuàng)建新請求
const promise = axios(config).finally(() => {
// 自動清理或保留緩存
if (!config.keepAlive) {
this.pool.delete(key);
}
});
// 緩存帶有效期
this.pool.set(key, {
promise,
expire: Date.now() + (config.cacheTTL || this.defaultTTL)
});
return promise;
}
// 手動清除緩存
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 // 自定義緩存時間
});
}
四、關(guān)鍵點(diǎn)解析
1. 請求唯一標(biāo)識設(shè)計
組合關(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)方式 | 適用場景 |
|---|---|---|
| 即時清理 | 請求完成后立即刪除 | 常規(guī)數(shù)據(jù)請求 |
| TTL 過期 | 檢查expire字段 | 需要短期緩存的數(shù)據(jù) |
| 手動清理 | 提供clearCache方法 | 明確知道數(shù)據(jù)變更時 |
| LRU 算法 | 維護(hù)使用記錄+最大數(shù)量限制 | 高頻請求且內(nèi)存敏感場景 |
3. 錯誤處理要點(diǎn)
.catch(error => {
// 特殊錯誤處理:網(wǎng)絡(luò)錯誤可保留短暫緩存
if (error.isNetworkError) {
setTimeout(() => this.pool.delete(key), 1000);
}
throw error;
});
五、適用場景對比
| 方案 | 優(yōu)點(diǎn) | 缺點(diǎn) | 最佳使用場景 |
|---|---|---|---|
| 基礎(chǔ)版 | 實(shí)現(xiàn)簡單、內(nèi)存占用少 | 缺乏高級控制 | 簡單頁面、少量API |
| 類封裝版 | 功能完善、擴(kuò)展性強(qiáng) | 實(shí)現(xiàn)復(fù)雜度較高 | 中大型項(xiàng)目、復(fù)雜場景 |
| 第三方庫(swr) | 開箱即用、功能豐富 | 需要學(xué)習(xí)新API | 需要快速實(shí)現(xiàn)的復(fù)雜緩存需求 |
六、延伸優(yōu)化方向
- 請求競速處理:
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'
}));
}
通過這種實(shí)現(xiàn)方式,可以有效解決以下問題:
- 減少 50%-90% 的重復(fù)網(wǎng)絡(luò)請求
- 避免組件重復(fù)渲染造成的性能損耗
- 保證多個組件間的數(shù)據(jù)一致性
- 降低服務(wù)端并發(fā)壓力
實(shí)際項(xiàng)目中可根據(jù)具體需求選擇基礎(chǔ)版或增強(qiáng)版實(shí)現(xiàn),建議配合 TypeScript 進(jìn)行類型約束以保證代碼健壯性。
以上就是通過共享Promise解決前端重復(fù)請求的代碼示例的詳細(xì)內(nèi)容,更多關(guān)于共享Promise解決重復(fù)請求的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
js實(shí)現(xiàn)類似MSN提示的頁面效果代碼分享
這篇文章主要介紹了模仿MSN消息提示的效果,推薦給大家,有需要的小伙伴可以參考下。2015-08-08
JS生態(tài)系統(tǒng)加速模塊解析賦能性能優(yōu)化探索
這篇文章主要為大家介紹了JS生態(tài)系統(tǒng)加速模塊解析賦能性能優(yōu)化探索,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01
利用H5api實(shí)現(xiàn)時鐘的繪制(javascript)
這篇文章主要為大家詳細(xì)介紹了利用H5api實(shí)現(xiàn)時鐘的繪制,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-09-09
JS使用iView的Dropdown實(shí)現(xiàn)一個右鍵菜單
這篇文章主要介紹了JS使用iView的Dropdown實(shí)現(xiàn)一個右鍵菜單功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2019-05-05

