在項目開發中,要求可以實現客戶多次提交,這樣的要求如何實現?
實現方法很多,比如添加 loading,在第一次點擊之后就無法再次點擊。另外一種方法就是給請求異步函數添加上一個靜態鎖,防止并發產生。這就是 ahooks 的 useLockFn 做的事情。
useLockFn
useLockFn用于給一個異步函數增加競態鎖,防止并發執行。
它的源碼比較簡單,如下所示:
import { useRef, useCallback } from 'react'; // 用于給一個異步函數增加競態鎖,防止并發執行。 function useLockFn<P extends any[] = any[], V extends any = any>(fn: (...args: P) => Promise<V>) { // 是否現在處于一個鎖中 const lockRef = useRef(false); // 返回的是增加了競態鎖的函數 return useCallback( async (...args: P) => { // 判斷請求是否正在進行 if (lockRef.current) return; // 請求中 lockRef.current = true; try { // 執行原有請求 const ret = await fn(...args); // 請求完成,狀態鎖設置為 false lockRef.current = false; return ret; } catch (e) { // 請求失敗,狀態鎖設置為 false lockRef.current = false; throw e; } }, [fn], ); } export default useLockFn;
這里的入參是異步函數,返回的是一個增加了競態鎖的函數。通過 lockRef 做一個標識位,初始化的時候它的值為 false。當正在請求,則設置為 true,可以影響下次再調用這個函數的時候,就直接 return,不執行原函數,從而達到加鎖的目的。
缺點
就雜每一個需要添加競態鎖的請求異步函數都手動加一遍。那如何解決?
答案是可以通過 axios 自動取消重復請求。
axios 自動取消重復請求
axios 取消請求
對于原生的 XMLHttpRequest 對象發起的 HTTP 請求,可以調用 XMLHttpRequest 對象的 abort 方法。
那么我們項目中常用的 axios 呢?它其實底層也是用的 XMLHttpRequest 對象,它對外暴露取消請求的 API 是 CancelToken。可以使用如下:
const CancelToken = axios.CancelToken; const source = CancelToken.source(); axios.post('/user/12345', { name: 'gopal' }, { cancelToken: source.token }) source.cancel('Operation canceled by the user.'); // 取消請求,參數是可選的
另外一種使用的方法是調用 CancelToken 的構造函數來創建 CancelToken,具體使用如下:
const CancelToken = axios.CancelToken; let cancel; axios.get('/user/12345', { cancelToken: new CancelToken(function executor(c) { cancel = c; }) }); cancel(); // 取消請求
如何自動取消重復的請求
怎么實現自動取消呢?答案是通過 axios 的攔截器。
請求攔截器:該類攔截器的作用是在請求發送前統一執行某些操作,比如在請求頭中添加 token 相關的字段。
響應攔截器:該類攔截器的作用是在接收到服務器響應后統一執行某些操作,比如發現響應狀態碼為 401 時,自動跳轉到登錄頁。
就看看做到方法:
第一步,定義幾個重要的輔助函數。
generateReqKey:用于根據當前請求的信息,生成請求 Key。只有 key 相同才會判定為是重復請求。這一點很重要,而且可能跟具體的業務場景有關,比如有一種請求,輸入框模糊搜索,用戶高頻輸入關鍵字,一次性發出多個請求,可能先發出的請求,最后才響應,導致實際搜索結果與預期不符。這種其實就只需要根據 URL 和請求方法判定其為重復請求,然后取消之前的請求就可以了。
這里我認為,如果有需要的話,可以暴露一個 API 給開發者進行自定義重復的規則。這里我們先根據請求方法、url、以及參數生成唯一的 key 去做。
function generateReqKey(config) { const { method, url, params, data } = config; return [method, url, Qs.stringify(params), Qs.stringify(data)].join("&"); }
addPendingRequest。用于把當前請求信息添加到 pendingRequest 對象中。
const pendingRequest = new Map(); function addPendingRequest(config) { const requestKey = generateReqKey(config); config.cancelToken = config.cancelToken || new axios.CancelToken((cancel) => { if (!pendingRequest.has(requestKey)) { pendingRequest.set(requestKey, cancel); } }); }
removePendingRequest。檢查是否存在重復請求,若存在則取消已發的請求。
function removePendingRequest(config) { const requestKey = generateReqKey(config); if (pendingRequest.has(requestKey)) { const cancelToken = pendingRequest.get(requestKey); cancelToken(requestKey); pendingRequest.delete(requestKey); } }
第二步,添加請求攔截器。
axios.interceptors.request.use( function (config) { removePendingRequest(config); // 檢查是否存在重復請求,若存在則取消已發的請求 addPendingRequest(config); // 把當前請求信息添加到pendingRequest對象中 return config; }, (error) => { return Promise.reject(error); } );
第二步,添加響應攔截器。
axios.interceptors.response.use( (response) => { removePendingRequest(response.config); // 從pendingRequest對象中移除請求 return response; }, (error) => { removePendingRequest(error.config || {}); // 從pendingRequest對象中移除請求 if (axios.isCancel(error)) { console.log("已取消的重復請求:" + error.message); } else { // 添加異常處理 } return Promise.reject(error); } );
到這里就可以通過 axios 完成了自動取消重復請求的功能。
思考與總結
useLockFn 也可以讓 hook或方法給請求函數添加競態鎖的方式解決重復請求的問題。可這個很需要依賴于開發者的習慣,假如沒有一些規則的約束,很難避免問題。
我們在通過 axios 攔截器以及其 CancelToken 功能,這樣就能夠在攔截器中自動將已發的請求取消,其實我們也要注意的是有一些接口就是需要重復發送請求,可以考慮加一下白名單功能,讓請求不進行取消。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/128257.html
起因 社會在不斷的向前,技術也在不斷的完善進步。從 React Hooks 正式發布到現在,越來越多的項目正在使用 Function Component 替代 Class Component,Hooks 這一新特性也逐漸被廣泛的使用。 這樣的解析是不是很熟悉,在日常中時常都有用到,但也有一個可以解決這樣重復的就是對數據請求的邏輯處理,對防抖節流的邏輯處理等。 另一方面,由于 Hoo...
之所以講這篇文章主要是為了加深對 React hooks 的理解。 因此,先要學習如何抽象自定義 hooks。構建屬于自己的 React hooks 工具庫。 且培養閱讀學習源碼的習慣,工具庫是一個對源碼閱讀不錯的選擇。 現在看下ahooks 是怎么封裝 cookie/localStorage/sessionStorage 的。 cookie ahooks 封裝了 useCookie...
這是講 ahooks 源碼的第一篇文章,簡要就是以下幾點: 加深對 React hooks 的理解。 學習如何抽象自定義 hooks。構建屬于自己的 React hooks 工具庫。 培養閱讀學習源碼的習慣,工具庫是一個對源碼閱讀不錯的選擇。 注:本系列對 ahooks 的源碼解析是基于v3.3.13。自己 folk 了一份源碼,主要是對源碼做了一些解讀,可見詳情。 第一篇主要介紹 a...
想必大家都能看得懂的源碼 ahooks 整體架構篇,且可以使用插件化機制優雅的封裝你的請求hook,現在我們就探討下ahooks 是怎么解決 React 的閉包問題的?。 React 的閉包問題 先來看一個例子: importReact,{useState,useEffect}from"react"; exportdefault()=>{ const[c...
摘要:原文鏈接有大量平均水平左右的工人可被選擇參與進來這意味著好招人有成熟的大量的程序庫可供選擇這意味著大多數項目都是既有程序庫的拼裝,標準化程度高而定制化場景少開發工具測試工具問題排查工具完善,成熟基本上沒有團隊愿意在時間緊任務重的項目情況 原文鏈接:http://pfmiles.github.io/blog/java-groovy-mixed/ 有大量平均水平左右的工人可被選擇、參與...
閱讀 561·2023-03-27 18:33
閱讀 750·2023-03-26 17:27
閱讀 647·2023-03-26 17:14
閱讀 603·2023-03-17 21:13
閱讀 537·2023-03-17 08:28
閱讀 1823·2023-02-27 22:32
閱讀 1315·2023-02-27 22:27
閱讀 2199·2023-01-20 08:28