<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
本文是深入淺出 ahooks 原始碼系列文章的第四篇,這個系列的目標主要有以下幾點:
注:本系列對 ahooks 的原始碼解析是基於 v3.3.13
。自己 folk 了一份原始碼,主要是對原始碼做了一些解讀,可見 詳情。
系列文章:
本文來探索一下 ahooks 的 useLockFn。
試想一下,有這麼一個場景,有一個表單,你可能多次提交,就很可能導致結果不正確。
解決這類問題的方法有很多,比如新增 loading,在第一次點選之後就無法再次點選。另外一種方法就是給請求非同步函數新增上一個靜態鎖,防止並行產生。這就是 ahooks 的 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 自動取消重複請求。
對於原生的 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 的攔截器。
具體的做法如下:
第一步,定義幾個重要的輔助函數。
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("&"); }
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); } }); }
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 功能,我們能夠在攔截器中自動將已發的請求取消,當然假如有一些介面就是需要重複傳送請求,可以考慮加一下白名單功能,讓請求不進行取消。
以上就是ahooks解決使用者多次提交方法範例的詳細內容,更多關於ahooks使用者多次提交的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45