likes
comments
collection
share

Promise批量请求&限制并发&避免阻塞&错误重连

作者站长头像
站长
· 阅读数 7

题目背景假设

一个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的话,如果这一批次里面有一个请求耗费大量时间,则会阻塞逻辑,像这样子~。

Promise批量请求&限制并发&避免阻塞&错误重连 所以为了避免同一批次的请求中存在某一个请求耗时非常长,进而阻塞这个批次。需要做一个动态的请求池。

思路大致为:

  • 设置一个总的请求池,里面有所有待发送的请求。一个批次的请求池里面限制30个请求。
  • 设置timerIntervel,每隔1S,从总的请求池中弹出30个,进行这一批次的请求发送。
  • 上一批次发送的请求失败的,重新进入总的请求池里面。
  • 当总的请求池里面清空后,所有请求均已发送完成。 像这样子~

Promise批量请求&限制并发&避免阻塞&错误重连

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

Promise批量请求&限制并发&避免阻塞&错误重连