JavaScript使用promise處理多重復(fù)請求
一、為什么要寫這個文章?
處理重復(fù)請求的文章想必大家也看過了很多,大多數(shù)都是分為在response返回之前發(fā)現(xiàn)重復(fù)請求就return掉的和使用節(jié)流/防抖來間接規(guī)避用戶頻繁操作兩種版本的。最近在使用的過程的中,發(fā)現(xiàn)這兩個版本在某些場景下還是有些局限性。
二、問題場景
如圖,我這個h5的頁面,頂部和底部都要顯示這個名片組件。這些名片的信息是通過一個接口來獲取的,當(dāng)這個組件在當(dāng)前頁面被初始化時,就會發(fā)生兩次重復(fù)的請求。


這時會面臨幾個抉擇:
1. 不對重復(fù)請求做任何處理。
- 缺點(diǎn)1:造成不必要的資源浪費(fèi),增大服務(wù)器的壓力
- 缺點(diǎn)2:http請求在瀏覽器中是有并發(fā)數(shù)限制的,如果頁面首屏的請求較多且沒有分層級加載的話,很容易造成請求阻塞,影響用戶第一時間看到主要內(nèi)容
2. 對重復(fù)請求直接return掉。這也是部分文章的做法,不過這種做法有種局限性,就是直接認(rèn)定后面的重復(fù)請求均為無效請求。
- 無效請求場景:用戶點(diǎn)擊了某個按鈕進(jìn)行查詢或保存,在請求結(jié)果返回之前,后面點(diǎn)擊基本都算是無效請求,這種請求就是應(yīng)該被阻止的。當(dāng)然,也可以通過在按鈕上添加節(jié)流/防抖來規(guī)避這個問題
- 為何不適用于目前場景:這兩個名片的組件都是需要數(shù)據(jù)來渲染的,如果第二次重復(fù)的請求被return了,其中一個組件的名片就會沒有數(shù)據(jù)。
3. 把請求從組件中抽離出來放到父級的業(yè)務(wù)頁面中,再以props的方式傳進(jìn)組件。
- 好處:只需要請求一次,兩個組件就可以共享一份數(shù)據(jù)。
- 局限性:只適用于單個業(yè)務(wù)頁面用到的情況。事實(shí)上這個組件很多個業(yè)務(wù)頁面在用,即使把請求的函數(shù)抽成公用的api,也是要在每個業(yè)務(wù)頁面初始化的時候調(diào)用一次,然后再以props的方式傳進(jìn)組件。
三、解決方式
核心思想
- 初始化一個handleList的數(shù)組
- 在請求發(fā)送前,根據(jù)入?yún)⑹欠裣嗤袛嗍欠駷橹貜?fù)請求
- 非重復(fù)請求:把改請求的參數(shù)和請求返回的Promise添加至數(shù)組中
- 重復(fù)請求:使用find查找直接返回對應(yīng)的Promise
- 請求完成后把handleList中之前添加的請求信息移除。
這個方案是什么都可以使用的,無論是使用axios、jq、fetch、小程序request。這里就寫實(shí)現(xiàn)的原理,使用時直接把對應(yīng)的代碼放到對應(yīng)的請求時機(jī)即可。
代碼示例
let handleList = [] // 請求列表
/**
* 模擬請求
* @author waldon
* @date 2020/6/9
*/
const httpRequest = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(`請求成功,時間戳為:${new Date().getTime()}`)
}, 1000)
})
}
/**
* 請求的相關(guān)處理
* @author waldon
* @date 2020/6/9
* @param {String} url -
* @param {Object} requestObj - 請求參數(shù)
* @returns {Promise} - 請求的promise
*/
function requestTest(url, requestObj = {}) {
// 因為入?yún)⒁话悴粫婕暗綇?fù)雜類型,JSON.stringify進(jìn)行序列化對比其實(shí)夠用了
// 有個局限性就是入?yún)⒌捻樞蚋淖兞司蜁绊懪袛?,不過這種特殊的改變一般在重復(fù)請求中不會出現(xiàn)
// 實(shí)在是有這種需求的,換成其他遞歸對比的api,lodash也有類似的api
const sameHandle = handleList.find(
(item) => item.url === url && JSON.stringify(item.requestObj) === JSON.stringify(requestObj)
)
if (sameHandle) {
// 遇到相同請求直接返回之前請求的promise
console.log(`存在重復(fù)請求,直接返回`)
return sameHandle.handle
}
const handle = new Promise((resolve, reject) => {
httpRequest()
.then((res) => {
resolve(res)
})
.catch((err) => {
reject(err)
})
.finally(() => {
// 無論請求結(jié)果如果,都需要把對應(yīng)的請求移除掉
handleList = handleList.filter(
(item) =>
item.url !== url && JSON.stringify(item.requestObj) !== JSON.stringify(requestObj)
)
})
})
handleList.push({ url, requestObj, handle })
return handle
}
// *******************************我是華麗的分割線 開始使用*******************************
const params = {
name: 'waldon'
}
requestTest('/ajax/sameUrl', params).then((res) => {
console.log(`首次請求結(jié)果`, res)
console.log(`handleList:`, handleList)
})
requestTest('/ajax/sameUrl', params).then((res) => {
console.log(`重復(fù)請求結(jié)果`, res)
console.log(`handleList:`, handleList) // 請求列表中始終只有一個請求
setTimeout(() => {
console.log(`請求完成后的handleList:`, handleList) // 請求完成handleList對應(yīng)的請求會被清除
}, 100)
})
setTimeout(() => {
// 特意延遲500ms請求,因為我們設(shè)置了接口1s才返回,所以應(yīng)該得到一樣的結(jié)果
requestTest('/ajax/sameUrl', params).then((res) => {
console.log(`重復(fù)請求結(jié)果`, res)
console.log(`handleList:`, handleList)
})
}, 500)
輸出結(jié)果
存在重復(fù)請求,直接返回
存在重復(fù)請求,直接返回
首次請求結(jié)果 請求成功,時間戳為:1621650375540
handleList: [
{
url: '/ajax/sameUrl',
requestObj: { name: 'waldon' },
handle: Promise { '請求成功,時間戳為:1621650375540' }
}
]
重復(fù)請求結(jié)果 請求成功,時間戳為:1621650375540
handleList: [
{
url: '/ajax/sameUrl',
requestObj: { name: 'waldon' },
handle: Promise { '請求成功,時間戳為:1621650375540' }
}
]
重復(fù)請求結(jié)果 請求成功,時間戳為:1621650375540
handleList: [
{
url: '/ajax/sameUrl',
requestObj: { name: 'waldon' },
handle: Promise { '請求成功,時間戳為:1621650375540' }
}
]
請求完成后的handleList: []
代碼地址 codepen
https://codepen.io/waldonUB/pen/ZEeeONM
注意的點(diǎn)
- 不要對response中的數(shù)據(jù)進(jìn)行增刪操作。因為重復(fù)請求返回Promise中的對象引用地址都是同一個,改動了就會造成數(shù)據(jù)污染。特殊情況時可以淺拷貝響應(yīng)結(jié)果再處理,或者是增加對應(yīng)的斷言。
- 處理重復(fù)的請求時,最好在log中提示一下,同時在組件中注釋好原因和使用場景,避免他人誤改
- 做好極端情況下,請求失敗的處理,設(shè)置有效時間置空和移除請求信息,避免因為閉包堆積過多無用的請求信息造成內(nèi)存泄漏。
到此這篇關(guān)于JavaScript使用promise處理多重復(fù)請求的文章就介紹到這了,更多相關(guān)js promise多重復(fù)請求內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JSONPath使用指南和實(shí)用技巧(掌握J(rèn)SON數(shù)據(jù)提?。?/a>
這篇文章主要給大家介紹了關(guān)于JSONPath使用指南和實(shí)用技巧的相關(guān)資料,JsonPath,類似于XPath在XML中的作用,其提供了對格式Json數(shù)據(jù)的解析能力,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-08-08
JavaScript 閉包機(jī)制詳解及實(shí)例代碼
這篇文章主要介紹了JavaScript 閉包機(jī)制詳解及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2016-10-10
JS中使用apply方法通過不同數(shù)量的參數(shù)調(diào)用函數(shù)的方法
這篇文章主要介紹了JS中使用apply方法通過不同數(shù)量的參數(shù)調(diào)用函數(shù)的方法的相關(guān)資料,需要的朋友可以參考下2016-05-05
Javascript基礎(chǔ)知識盲點(diǎn)總結(jié)之函數(shù)
函數(shù)是由事件驅(qū)動的或者當(dāng)它被調(diào)用時執(zhí)行的可重復(fù)使用的代碼塊。這篇文章主要介紹了Javascript基礎(chǔ)知識盲點(diǎn)總結(jié)之函數(shù)的相關(guān)資料2016-05-05
JS中postcss插件實(shí)現(xiàn)vw適配的方法
PostCSS是一個用JavaScript編寫的插件工具,它可以幫助我們對CSS進(jìn)行模塊化、自動化處理和優(yōu)化,這篇文章主要介紹了JS中postcss插件實(shí)現(xiàn)vw適配,需要的朋友可以參考下2025-01-01

