Promise批量请求&限制并发&避免阻塞&错误重连
题目背景假设
一个GIS地图上面需要显示很多个图标,目前数据只有地名,需要向高德地图发送请求获取这些地名的经纬度,一次请求只能查询一次经纬度,每次请求查询耗时不确定,有超时的可能,接口qps限制为30/s。如何使用promise编写一个批量查询的请求方法?
本质:使用promise控制请求并发数,超时重发,限制重发次数。
request
预先准备一个request函数,waite小于5000,请求成功;wait超过5000,promise会reject,如果reconnect为true,则代表是重连请求,默认重连一次就成功。
export default function request(wait,reconnect) {
return new Promise(function (resolve, reject) {
if(wait > 5000) {
if(reconnect) {
console.log('触发重连,重连的是:',wait);
}else{
reject(wait)
}
}
setTimeout(function () {
resolve(wait);
}, wait);
});
}
const urls = new Array(100).fill().map((_,i)=>i*100).sort(()=>Math.random() - 0.5)
let reqs = urls.map(item=>request(item))
// reqs为请求数组,按100、200、300递增100到10000,共计100个请求
控制并发数量
可以将这些请求分割成为二维数组,二维数组的单个元素长度为控制并发的数量,遍历二维数组,每一次遍历使用promise.all()来发送请求。即可完成并发数量的控制。
ps:但是用promise.all的话,如果这一批次里面有一个请求耗费大量时间,则会阻塞逻辑,像这样子~。
所以为了避免同一批次的请求中存在某一个请求耗时非常长,进而阻塞这个批次。需要做一个动态的请求池。
思路大致为:
- 设置一个总的请求池,里面有所有待发送的请求。一个批次的请求池里面限制30个请求。
- 设置timerIntervel,每隔1S,从总的请求池中弹出30个,进行这一批次的请求发送。
- 上一批次发送的请求失败的,重新进入总的请求池里面。
- 当总的请求池里面清空后,所有请求均已发送完成。 像这样子~
mutiReq
具体代码实现如下
const mutiReq = (max,interval,reqs) => {
let all = reqs.length
let timer = setInterval(()=>{
for(let i=0;i<max;i++){
if(all===0){
clearInterval(timer)
break
}
if(!reqs.length) continue
let req = reqs.pop()
req.then(res=>{
all--
},err=>{
reqs.push(request(err,true))
})
}
},interval)
}
ps:注意不能将请求数组的长度为0作为定时器的条件,因为有可能这个请求会失败,失败后还会回到请求数组里面,所以用了all做成功请求的记录。
实验:控制台打印以及html上结果可视化。
/**
* mutiReq
*/
import request from "../utils/request.js";
import {setKey} from "../utils/utils.js"
const urls = new Array(100).fill().map((_,i)=>i*100).sort(()=>Math.random() - 0.5)
let reqs = urls.map(item=>request(item))
const MutiReq = () => {
const [list,setList] = React.useState([])
React.useEffect(()=>{
mutiReq(30,1000,reqs)
},[])
const mutiReq = (max,interval,reqs) => {
let count = 0,all = reqs.length
let timer = setInterval(()=>{
count++
let temp = []
for(let i=0;i<max;i++){
if(all===0){
clearInterval(timer)
break
}
if(!reqs.length) continue
let req = reqs.pop()
temp.push(req)
let batch=count
req.then(res=>{
console.log(res);
all--
setList(current=>[...current,{value:res,batch:batch}])
},err=>{
reqs.push(request(err,true))
})
}
console.log(`第${count}批请求,个数为${temp.length}`);
},interval)
}
return (
<div>
<h2>批量并发请求控制批次时间</h2>
{list.map(item=>(
<p key={setKey(item)}>
{'value:'+item.value+' key:'+setKey(item) +' batch:'+item.batch}
</p>
))}
</div>
)
}
export default MutiReq
转载自:https://juejin.cn/post/7256651192984469564