JavaScript使用promise處理多重複請求
一、為什麼要寫這個文章?
處理重複請求的文章想必大家也看過了很多,大多數都是分為在response返回之前發現重複請求就return掉的和使用節流/防抖來間接規避使用者頻繁操作兩種版本的。最近在使用的過程的中,發現這兩個版本在某些場景下還是有些侷限性。
二、問題場景
如圖,我這個h5的頁面,頂部和底部都要顯示這個名片元件。這些名片的資訊是通過一個介面來獲取的,當這個元件在當前頁面被初始化時,就會發生兩次重複的請求。
這時會面臨幾個抉擇:
1. 不對重複請求做任何處理。
- 缺點1:造成不必要的資源浪www.cppcns.com費,增大伺服器的壓力
- 缺點2:http請求在瀏覽器中是有併發數限制的,如果頁面首屏的請求較多且沒有分層級載入的話,很容易造成請求阻塞,影響使用者第一時間看到主要內容
2. 對重複請求直接return掉。這也是部分文章的做法,不過這種做法有種侷限性,就是直接認定後面的重複請求均為無效請求。
- 無效請求場景:使用者點選了某個按鈕進行查詢或儲存,在請求結果返回之前,後面點選基本都算是無效請求,這種請求就是應該被阻止的。當然,也可以通過在按鈕上新增節流/防抖來規避這個問題
- 為何不適用於目前場景:這兩個名片的元件都是需要資料來渲染的,如果第二次重複的請求被return了,其中一個元件的名片就會沒有資料。
3. 把請求從元件中抽離出來放到父級的業務頁面中,再以props的方式傳進元件。
- 好處:只需要請求一次,兩個元件就可以共享一份資料。
- 侷限性:只適用於單個業務頁面用到的情況。事實上這個元件很多個業務頁面在用,即使把請求的函式抽成公用的api,也是要在每個業務頁面初始化的時候呼叫一次,然後再以props的方式傳進元件。
三、解決方式
核心思想
- 初始化一個handleList的陣列
- 在請求傳送前,根據入參是否相同判斷是否為重複請求
- 非重複請求:把改請求的引數和請求返回的Promise新增至陣列中
- 重複請求:使用find查詢直接返回對應的Promise
- 請求完成後把handleList中之前新增的請求資訊移除。
這個方案是什麼都可以使用的,無論是使用axios、jq、fetch、小程式request。這裡就寫實現的原理,使用時直接把對應的程式碼放到對應的請求時機即可。
程式碼示例
let handleList = [] // 請求列表 /** * 模擬請求 * @author waldon * @date 2020/6/9 */ const httpRequest = () => { return new Promise((resolve) => { setTimeout(() => { resolve(`請求成功,時間戳為:${new Date().getTime()}`) },1000) }) } /** * 請求的相關處理 * @author waldon * @date 2020/6/9 * @param {String} url - * @param {Object} requestObj - 請求引數 * @returns {Promise} - 請求的promise */ function requestTest(url,requestObj = {}) { // 因為入參一般不會涉及到複雜型別,jsON.stringify進行序列化對比其實夠用了 // 有個侷限性就是入參的順序改變了就會影響判斷,不過這程式設計客棧種特殊的改變一般在重複請求中不會出現 // 實在是有這種需求的,換成其他遞迴對比的api,lodash也有類似的api const sameHandle = handleList.find( (item) => item.url === url && JSON.stringify(item.requestObj) === JSON.stringify(requestObj) ) if (sameHandle) { // 遇到相同請求直接返回之前請求的promise console.log(`存在重複請求,直接返回`) return sameHandle.handle } const handle = new Promise((resolve,reject) => { httpRequest() .then((res) => { resolve(res) }) .catch((err) => { reject(err) }) .finally(() => { // 無論請求結果如果,都需要把對應的請求移除掉 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(`首次請求結果`,res) console.log(`handleList:`,handleList) }) requestTest('/ajax/sameU程式設計客棧rl',params).then((res) => { console.log(`重複請求結果`,handleList) // 請求列表中始終只有一個請求 setTimeout(() => { console.log(`請求完成後的handleList:`,handleList) // 請求完成handleList對應的請求會被清除 },100) }) setTimeout(() => { // 特意延遲500ms請求,因為我們設定了介面1s才返回,所以應該得到一樣的結果 requestTest('/ajax/same程式設計客棧Url',params).then((res) => { console.log(`重複請求結果`,res) console.log(`handleList:`,handleList) }) },500)
輸出結果
存在重複請求,直接返回
存在重複請求,直接返回
首次請求結果 請求成功,時間戳為:1621650375540
handleList: [
{
url: '/ajax/sameUrl',
requestObj: { name: 'waldon' },
handle: Promise { '請求成功,時間戳為:1621650375540' }
}
]
重複請求結果 請求成功,時間戳為:1621650375540
handleList: [
{
url: '/ajax/sameUrl',
handle: Promise { '請求成功,時間戳為:1621650375540' }
}
]
請求完成後的handleList: []
程式碼地址 codepen
https://codepen.io/waldonUB/pen/ZEeeONM
注意的點
- 不要對response中的資料進行增刪操作。因為重複請求返回Promise中的物件引用地址都是同一個,改動了就會造成資料汙染。特殊情況時可以淺拷貝響應結果再處理,或者是增加對應的斷言。
- 處理重複的請求時,最好在log中提示一下,同時在元件中註釋好原因和使用場景,避免他人誤改
- 做好極端情況下,請求失敗的處理,設定有效時間置空和移除請求資訊,避免因為閉包堆積過多無用的請求資訊造成記憶體洩漏。
到此這篇關於javascript使用promise處理多重複請求的文章就介紹到這了,更多相關js promise多重複請求內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!