1. 程式人生 > 其它 >利用 Promise.race 提前終止多餘非同步請求

利用 Promise.race 提前終止多餘非同步請求

在開發中複雜列表或圖表,加上各種條件、篩選是個常見的需求,介面響應速度通常比較慢。

一般篩選條件觸發重新整理的操作都會加上防抖,避免短時間多次重複請求,極端情況下防抖並非萬無一失的策略。我們來模擬一下:

防抖等待時間為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
  })
}