首页 > 其他分享 >每天一个小知识,今日知识-如何设计一个并发请求控制函数

每天一个小知识,今日知识-如何设计一个并发请求控制函数

时间:2023-08-24 19:45:29浏览次数:38  
标签:function const 请求 知识 token 并发 length result

假如给你一个数组,里面是请求路径,如何设计一个函数去控制这些请求的并发呢?

这里我们用的请求路径为https://jsonplaceholder.typicode.com/todos来模拟

      const reqArr = [];
      for (let i = 1; i <= 10; i++) {
        reqArr.push(`https://jsonplaceholder.typicode.com/todos/${i}`);
      }

 

第一种方案:基于Promise实现:

首先定义一个函数,并且确定其形参,第一个为传入的请求路径数组,第二个为需要限制的并发数,并且返回值为Promise。

  function limitReqFn(urlArray, limitNum){
return new Promise(resolve)=>{}
}

 

 

第一步:首先我们需要对传入的路径数组判断一下是否为空。 

         if (urlArray.length === 0) {//如果为空就直接resolve出去
            resolve([]);
            return;
          }

  

第二步:我们需要定义一个result数组用于接收请求响应结果,并且定义当前请求路径的index值下标currentRequetIndex,以及一个异步请求函数request

     const result = [];
     let currentRequetIndex = 0;
     async function request(){}

 

第三部:完善request逻辑,核心逻辑就是拿到当前请求的路径并且进行网络请求,然后将结果放到result里,需要注意的就是拿到请求结果res后不能直接用push方法放到result里,因为返回的顺序可能不一致。 需要拿一个临时变量i存储currentRequetIndex,然后再将结果数组result的下标i的值设为请求返回的结果res

       async function request() {
            if (currentRequetIndex === urlArray.length) { //当索引下标等于传入的url数组时返回
              return;
            }
            const i = currentRequetIndex;//临时存储currentRequestIndex
            const currentReqUrl = urlArray[currentRequetIndex];//拿到当前请求路径
            currentRequetIndex++; //将currentRequestIndex进行+1,指向下一个请求路径
            try {
              const res = await fetch(currentReqUrl);
              result[i] = res;//将结果赋值给result数组
            } catch (error) {
              result[i] = error;
            } finally {//请求完后判断result的长度是否和传入的数组相同,然后返回结果
              if (result.length === urlArray.length) {
                resolve(result);
              }
              request();//将下一个路径拿来请求
            }
          }

 

第四部:设置并发数量,并且进行请求

       const times = Math.min(limitNum, urlArray.length);//将limitNum和传入的数组长度做一下比较
          for (let i = 0; i < times; i++) {
            request();
          }

 

完整代码:

      const reqArr = [];
      for (let i = 1; i <= 10; i++) {
        reqArr.push(`https://jsonplaceholder.typicode.com/todos/${i}`);
      }
      function limitReqFn(urlArray, limitNum) {
        return new Promise((resolve) => {
          if (urlArray.length === 0) {
            resolve([]);
            return;
          }
          const result = [];
          let currentRequetIndex = 0;
          async function request() {
            if (currentRequetIndex === urlArray.length) {
              return;
            }
            const i = currentRequetIndex;
            const currentReqUrl = urlArray[currentRequetIndex];
            currentRequetIndex++;
            try {
              const res = await fetch(currentReqUrl);
              result[i] = res;
            } catch (error) {
              result[i] = error;
            } finally {
              if (result.length === urlArray.length) {
                resolve(result);
              }
              request();
            }
          }
          const times = Math.min(limitNum, urlArray.length);
          for (let i = 0; i < times; i++) {
            request();
          }
        });
      }
      limitReqFn(reqArr,2).then((data)=>console.log(data));


接下来我们到浏览器里去试一试,f12打开network,切换到fast 3G模拟慢网

 

  

第二种方案:采用数组令牌的方式。

第一步:我们根据传入的并发条数创建一个对应的token数组,在请求的时候,需要从数组中拿一个token作为标识,然后请求完成后将token归还回去,然后定义一个等待队列,一个执行队列。

    function limitRequest(rqArr, length) {
        const tokenKey = Array.from({ length }, (v, k) => `token${k}`);//创建token标识数组
        const backToken = function (token) { //归还token的方式
          tokenKey.push(token);
        };
        const getTokenKey = function () { //获取一个token
          return tokenKey.splice(0, 1)[0];
        };
        let waitQueen = [];//等待队列
        let exectuor = [];//执行队列
}

  

第二步:我们需要定义一个方法将对应的请求带上token存入执行队列,如果没拿到token的请求就放入等待队列。

 

       const generateFn = function (arr, clear=false) {
            if (clear) {// 清空队列 
              exectuor = [];
            }
            for (let i = 0; i < arr.length; i++) {
              if (tokenKey && tokenKey.length) {//如果还有token剩余
                const reqObj = {
                  token: getTokenKey(),
                  url: arr[i],
                };
                exectuor.push(reqObj);//将请求添加到执行队列
              } else {
                waitQueen.push(rqArr[i]);//没有拿到token就添加到等待队列
              }
            }
                              runExectuor();//执行队列生成完后,就到执行队列里去请求
          };

  

第三步:我们已经生成了执行队列,这个时候就需要定义一个执行队列里请求的方法。

    const runExectuor = function () {
            for (const fn of exectuor) {
              fetch(fn["url"]).then((v) => {
                backToken(fn.token);//执行完成一个请求后就归还token
                if (waitQueen && waitQueen.length) {//如果等待队列里有请求就拿出来放到执行队列里
                  generateFn(waitQueen.splice(0, 1), true);//每次生成需要清空队列
                }
              });
            }
          };

  

完整代码:

      function limitRequest(rqArr, length) {
        const tokenKey = Array.from({ length }, (v, k) => `token${k}`);
        const result = [];
        const backToken = function (token) {
          tokenKey.push(token);
        };
        const getTokenKey = function () {
          return tokenKey.splice(0, 1)[0];
        };
        let waitQueen = [];
        let exectuor = [];
          const generateFn = function (arr, type=false) {
            if (type) {
              exectuor = [];
            }
            for (let i = 0; i < arr.length; i++) {
              if (tokenKey && tokenKey.length) {
                const reqObj = {
                  token: getTokenKey(),
                  url: arr[i],
                };
                exectuor.push(reqObj);
              } else {
                waitQueen.push(rqArr[i]);
              }
            }
            runExectuor();
          };
          const runExectuor = function () {
            for (const fn of exectuor) {
              fetch(fn["url"]).then((v) => {
                backToken(fn['token']);
                if (waitQueen && waitQueen.length) {
                  generateFn(waitQueen.splice(0, 1), true);
                }
              });
            }
          };
          generateFn(rqArr);
      }
      const reqArr = [];
      for (let i = 1; i <= 10; i++) {
        reqArr.push(`https://jsonplaceholder.typicode.com/todos/${i}`);
      }
      limitRequest(reqArr,2);

  

好啦,今天就分享这2种解决并发控制方法和思路。

  

  

标签:function,const,请求,知识,token,并发,length,result
From: https://www.cnblogs.com/micbin/p/17655006.html

相关文章

  • The database operation was expected to affect 1 row(s), but actually affected 0
    Thedatabaseoperationwasexpectedtoaffect1row(s),butactuallyaffected0row(s);解决乐观并发1.乐观并发EFCore实现乐观并发,假定并发冲突相对较少。与悲观方法(即先锁定数据,然后才继续修改数据)不同,乐观并发不需要锁定,而是安排数据修改在保存时失败(如果数据自......
  • Java限制某段时间内某个请求的次数(代码库)
    关键就是统计次数技巧:1、使用guavacache缓存来计数2、利用引用变量的特性,减少put,只使用get如果重新put赋值,缓存的时间会刷新,比如下面例子的b,一共输出了7次,而a只输出了5次importcom.google.common.cache.Cache;publicclassTest2{privatestaticCache<String,Tes......
  • 【知识整理】基于Springboot的Kafka消费者动态操作
    基于Springboot的Kafka消费者动态操作1.问题​ 在基于Springboot开发Kafka相关业务时,遇到如下业务场景:执行部分操作时,如停止服务替换镜像、执行特殊业务处理等,需要先停止Consumer接收Kafka消息,待处理完成后再开启Consumer接续接收Kafka消息为并发消费Kafka消息,可通过配置sp......
  • 14.2 并发与竞争实验
    一、原子操作实验  这节使用原子操作来实现对LED设备的互斥访问,也就是只有一个应用程序能使用LED。 1.实验程序编写  因为是12章已经修改了设备树,所以这里暂时不用修改。  在/linux/atk-mpl/Drivers该目录下创建7_atomic子目录,并且把5_gpioled里面的gpio......
  • 并发问题和实现Callable接口
    并发1.初识并发问题//多个线程同时操作一个对象//买火车票//发现问题:多个线程操作同一个资源的情况下,线程不安全publicclassTestThread4implementsRunnable{  //票数  privateintticketNums=10;​  publicvoidrun(){    while(true){  ......
  • 【知识整理】Springboot启动扩展点
    SpringBoot启动扩展点整理1.前言​ 在Springboot服务启动阶段,Springboot提供了许多扩展点。在实际的业务开发过程中,部分特殊的业务需求需要再Springboot服务启动过程中动态的加载配置或者执行业务处理,特此将常用的Springboot启动扩展点做一个简单的整理。2.准备阶段2.1Env......
  • Nginx内置lua版OpenResty拦截转发请求Redis等操作
    Nginx内置lua版OpenResty拦截转发请求Redis等操作1下载并安装OpenRestyhttp://openresty.org/cn/download.html2下载lua-resty-http-0.17.1库以让openresty的lua支持外部http访问能力lua-resty-http-0.17.11下载lua-resty-http-0.17.12然后将文件中lua-resty-http......
  • 《asyncio 系列》1. 什么是 asyncio?如何基于单线程实现并发?事件循环又是怎么工作的?
    https://www.cnblogs.com/traditional/p/17357782.html楔子许多应用程序,尤其在当今的Web应用程序领域,严重依赖IO操作。这些类型的操作包括从Intermet下载网页的内容、通过网络与一组微服务进行通信,或者针对MySOL、Postgres等数据库同时运行多个查询。Web请求或与微服......
  • Golang 需要注意的知识点
    云笔记链接地址 go的协程轻量级体现在哪(1)goroutine是轻量级的用户态线程,上下文切换代价小go将goroutine的调度维持在用户态常规线程切换会导致用户态程序代码和内核态操作系统调度程序的切换只涉及PC(程序计数器,标记当前执行的代码的位置)SP(当前执行的函数堆栈栈......
  • Python-保存request请求为各种文件
    文件下载相关工具importjsonimportrequestsclassCustomFileTools(object):defdownload_json_file(self,json_url,save_path):"""下载json文件并保存"""json_req=requests.get(json_url)ifjso......