在项目开发中,要求可以实现客户多次提交,这样的要求如何实现?
实现方法很多,比如添加 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 功能,这样就能够在拦截器中自动将已发的请求取消,其实我们也要注意的是有一些接口就是需要重复发送请求,可以考虑加一下白名单功能,让请求不进行取消。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/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/ 有大量平均水平左右的工人可被选择、参与...
阅读 547·2023-03-27 18:33
阅读 732·2023-03-26 17:27
阅读 630·2023-03-26 17:14
阅读 591·2023-03-17 21:13
阅读 521·2023-03-17 08:28
阅读 1801·2023-02-27 22:32
阅读 1292·2023-02-27 22:27
阅读 2178·2023-01-20 08:28