找回密码
 会员注册
查看: 18|回复: 0

关于请求这件小事

[复制链接]

3

主题

0

回帖

10

积分

新手上路

积分
10
发表于 2024-10-10 10:34:24 | 显示全部楼层 |阅读模式
关于请求这件小事 江苏苏 Goodme前端团队 Goodme前端团队 定期分享一些团队对前端领域的沉淀 48篇内容 2024年08月26日 08:00 浙江 一、引言 在日常开发过程中,经常与服务器进行交互。但其实,看似简单的请求,也有一些可以思考的点。大家都知道,浏览器是有并发限制的,最大并发数量是6-8个。那么,引出我们的第一个问题,针对一些相似请求,怎么合并请求?二、案例 1. 相似请求合并请求1.1. 背景最近在梳理登录功能的过程中,发现页面中会多次调用usePromission这个接口来判断当前登录人是否有某个功能的权限。但是,如果页面中有多个功能点需要判断权限,那我们就要多次调用usePromission,也就意味着我们要发起多个相似的请求。但实际上,在实际应用中,针对一些复杂的应用,功能权限点可能很多,再加上页面的一些本身的查询,我们就有可能会超出浏览器的并发限制。所以,针对需要判断是否有某项功能的权限点,我们需要合并请求。大致流程如下:1.2. 实现:相似请求合并请求模拟请求functionmockRequest(params){returnnewPromise((resolve)=>{setTimeout(()=>{resolve(params.map((p)=>{return{code:p,isAllow:Math.random()>0.5,};}));},1000);});}其余代码//使用Promise.resolve()创建一个promise实例,我们用它将一个任务添加到微任务队列letp=Promise.resolve();letisFlushing=false;letparams=newSet();functionexecute(){if(isFlushing)return;isFlushing=true;p=p.then(()=>{returnmockRequest([...params]);});}functionprint(param){params.add(param);关于请求这件小事execute();returnp.then((data)=>{console.loh('data',data);returndata.find((item)=>item.code===param).isAllow;});}functiontest(){for(leti=0;i{console.log(isAllow);});}}test();执行顺序如下:i = 0,执行print方法,将参数塞入全局的params中;执行execute方法,设置全局的isFlushing为true,同时,将promise pending赋值给p。继续往下执行i = 1,执行print方法,将参数塞入全局的params中;执行execute方法,此时isFlushing为true,直接返回。继续往下执行i = 2,3,4,5,6,7,8,9 同i = 1i = 10,i {const[data,setData]=useState([]);constfetch=(num)=>{//模拟接口返回,为了明显看到效果,在这获取6条数据比获取3条数据慢returnnewPromise((resolve)=>{setTimeout(()=>{resolve(Array(num).fill({}).map((item,index)=>{return{index}}))},num*500);})}constgetData=(num)=>{fetch(num).then((data)=>{setData(data);})}return({getData(3)}}>点击获取3条数据{getData(6)}}>点击获取6条数据{data.map(({index})=>{return渲染的数据{index+1}})});};exportdefaultClickCount;2.4. 解决方案只有当当前请求是最新请求时,结果才返回,否则结果就不返回。那怎么知道我当前的返回是最新一次的返回呢?可以通过useRef。具体如下:全局存储当前请求次数 const requestPool = useRef(0);每次请求+1requestPool.current += 1; // 每次请求加1const requestId = requestPool.current; // 记录当前是第几次请求请求完成后,比较当前是否时最新返回requestId === requestPool.currentimport{Button}from"antd";import{useRef,useState}from"react";constClickCount=()=>{const[data,setData]=useState([]);constrequestPool=useRef(0);//记录最新请求constfetch=(num)=>{returnnewPromise((resolve)=>{setTimeout(()=>{resolve(Array(num).fill({}).map((item,index)=>{return{index}}))},num*500);})}constgetData=(num)=>{requestPool.current+=1;//每次请求加1constrequestId=requestPool.current;//记录当前是第几次请求fetch(num).then((data)=>{console.log('data',data)if(requestId===requestPool.current){//只有当当前请求时最新请求时,才返回setData(data);}})}return({getData(3)}}>点击返回3条数据{getData(6)}}>点击返回6条数据{data.map(({index})=>{return渲染的数据{index+1}})});};exportdefaultClickCount;页面效果就算返回6条数据更慢,但页面只会展示最后一次请求获取的数据。3. 不同请求,并发请求3.1. 背景尽管业务中不常见,但也可以考虑一下不同请求的并发的情况。例如:页面复杂,初始时加载多个接口,再例如下载中心下载多个不同的文件等,都需要考虑浏览器的并发限制。3.2. 实现:不同请求,并发3.3. 例子模拟:页面中存在一个按钮(测试),点击触发6个模拟请求,限制同时并发2个。import{Button}from"antd"constDemo=()=>{//模拟请求constfetch=(num)=>{returnnewPromise((resolve)=>{setTimeout(()=>{console.log('num',num);resolve(num)//注意:此处为了模拟接口的快慢,num为1、3、5时接口返回为1s,2、4、6 时接口返回是3s},1000+(num%2===02000:0));})}consttask1=()=>{returnfetch(1);}consttask2=()=>{returnfetch(2)}consttask3=()=>{returnfetch(3);}consttask4=()=>{returnfetch(4)}consttask5=()=>{returnfetch(5);}consttask6=()=>{returnfetch(6)}//一次点击,触发6个请求consttest=async()=>{task1();task2();task3();task4();task5();task6();}return(测试)}exportdefaultDemo;此时,执行顺序为1s后打印1,3,5。再等2s后打印2、4、6那怎么实现并发请求数量为2的请求呢?3.4. 解决方案每次最多并发2个请求,请求完成了一个,那就继续发起下一个请求。基于此:定义以下几个参数activeCount 当前正在请求的个数result 并发返回的结果。这里需要注意,接口的返回是有快慢的,所以需要保证接口的返回跟请求的先后对应的上哦初始,当前正在请求的个数小于默认并发的最大个数,发起请求发起请求后,正在请求的个数+1,只要请求完成了,请求的个数就-1。判断是否有下一个请求,存在下个请求,继续发起请求判断请求是否全都结束,全部结束,将结果返回代码如下:import{Button}from"antd"constDemo=()=>{constfetch=(num)=>{returnnewPromise((resolve)=>{setTimeout(()=>{resolve(num)},1000+(num%2===02000:0));})}//concurrency默认并发的最大个数constrequestQueue=(concurrency)=>{return(urls)=>{letresult=[];//存储所有请求的结果letcurIndex=0;//请求的下标letactiveCount=0;//当前活跃请求的计数returnnewPromise((resolve,reject)=>{constdequeue=(url,index)=>{activeCount++;url().then((data)=>{result[index]=data;}).finally(()=>{activeCount--if(curIndex{returnfetch(1);}consttask2=()=>{returnfetch(2)}consttask3=()=>{returnfetch(3);}consttask4=()=>{returnfetch(4)}consttask5=()=>{returnfetch(5);}consttask6=()=>{returnfetch(6)}consttest=async()=>{constdata=awaitenqueue([task1,task2,task3,task4,task5,task6])console.log('data',data);}return(测试)}exportdefaultDemo;此时,执行顺序是每隔1s分别出现 1、3、2、5、4。最后等待2s出现6。然后打印最终的返回结果【1、2、3、4、5、6】不重要的解析:初始,同时发起task1 & task2,task1的返回时间是1s,task2的返回时间3s等待1s,task1执行完之后,打印出1;task3开始执行,此时task2还剩2s,task3的返回时间1s等待1s,task3执行完之后,打印出3;task4开始执行,此时task2还剩1s,task4返回时间3s等待1s,task2执行完之后,打印出2;task5开始执行,此时task4还剩2s,task5返回时间1s等待1s,task5执行完之后,打印出5;task6开始执行,此时task4还剩1s,task6返回时间3s等待1s,task4执行完之后,打印出4;等待2s,task6执行完之后,打印出6;三、总结关于请求这件事,一个很小的切入点,但仔细想想,也是有一些值得思考的点存在。相似请求合并请求,不同请求并发请求,不同请求展示最新结果等等等。关于请求,当然远不止于这些。还有很多,但由于篇幅(我累了)原因,就到这吧。最后一句话:知识点无处不在,勤于思考,勤能补拙,天道酬勤。共勉~
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2024-12-29 15:29 , Processed in 2.361015 second(s), 26 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表