利用 Promise.race 提前終止多餘非同步請求
阿新 • • 發佈:2021-10-09
在開發中複雜列表或圖表,加上各種條件、篩選是個常見的需求,介面響應速度通常比較慢。
一般篩選條件觸發重新整理的操作都會加上防抖,避免短時間多次重複請求,極端情況下防抖並非萬無一失的策略。我們來模擬一下:
防抖等待時間為300毫秒,使用者第一次改變條件,第一個請求正常發起,較為耗時需要1秒;300毫秒時使用者第二次改變條件,沒有觸發防抖機制,正常發起請求,耗時較短200毫秒響應結果,此時頁面上顯示為第二次改變的條件和第二次請求的資料,正常;1秒後第一次請求完成響應,此時頁面上圖表資料為第一次請求的內容,但是條件扔為第二次的,異常。
理想情況下,當第二次請求發起後第一次請求結果將不渲染。
如何實現呢?大部分專案中的axios或其他請求工具,都被promise包裝,無法用原生的方法將請求提前終止。
Promise.race 接受一個promise陣列作為引數,包裝一個新的promise例項,新的promise例項將返回陣列中最快結束的結果。我們可以利用Promise.race的競速機制防止promise進入then回撥,變相實現提前終止非同步請求。實現思路:
將正常的 promise 和一個“假” promise 用 Promise.race 包裝,並利用閉包儲存 “假” promise 中的 reject 方法。呼叫reject後,整個promise進入失敗階段,被終止。
程式碼:
/** * 解決非同步操作衝突的問題 * 多次觸發後,丟棄未響應的結果,只有最新的一次會進入 then回撥中 * @template T * @param {T & function} fn 需要包裝的非同步操作 * @return T */ export function useLatestCall(fn) { // “假” promise 中的 reject 方法 let prePromiseReject return function() { // 如果存在,就行終止 prePromiseReject?.(new Error('新的promise執行,舊promise被丟棄')) // 建立“假” promise,並將reject賦值給閉包變數 const fakePromise = new Promise((resolve, reject) => (prePromiseReject = reject)) return Promise.race([fakePromise, fn(...arguments)]) .finally(() => (prePromiseReject = null)) } }
業務中使用(vue 或者 react中具體寫法不同,大致如此):
const fetchData = useLatestCall(getXXXXX)
const reloadPage = () => {
// 只有最近觸發的一次會進入 then 回撥
fetchData(params).then(res => {
// xxx
})
}