请求并发控制

请求并发数量控制

并发限制

要求:多个请求做并发限制,请求完成后执行回调

思路

首次循环启动能够执行的任务

取出能执行的任务推入执行器执行

执行器更新当前并发数,并且再请求完成时继续取出任务推入执行器

当所有请求完成时,触发回调函数

      function sendRequest(requestList, limits, callback) {
        const promises = requestList.slice(); // 取得请求list(浅拷贝一份)
        // 得到开始时,能执行的并发数
        const concurrentNum = Math.min(limits, requestList.length);
        let concurrentCount = 0; // 当前并发数
        // 第一次先跑起可以并发的任务
        const runTaskNeeded = () => {
          let i = 0;
          // 启动当前能执行的任务
          while (i < concurrentNum) {
            i++;
            runTask();
          }
        };
        // 取出任务并且执行任务
        const runTask = () => {
          const task = promises.shift();
          task && runner(task);
        };
        // 执行器
        // 执行任务,同时更新当前并发数
        const runner = async (task) => {
          try {
            concurrentCount++;
            const res = await task();
            console.log(res);
          } catch (error) {
            console.log(error);
          } finally {
            // 并发数--
            concurrentCount--;
            // 添加下一个任务
            picker();
          }
        };
        // 添加下一个任务
        const picker = () => {
          // 任务队列里还有任务并且此时还有剩余并发数的时候 执行
          if (concurrentCount < limits && promises.length > 0) {
            // 继续执行任务
            runTask();
            // 队列为空的时候,并且请求池清空了,就可以执行最后的回调函数了
          } else if (promises.length == 0 && concurrentCount == 0) {
            // 执行结束
            callback && callback();
          }
        };
        // 入口执行
        runTaskNeeded();
      }

测试一下上面的代码

      function axios(url) {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            if (Math.random() > 0.6) {
              resolve("成功");
            } else {
              reject("失败");
            }
          }, Math.random() * 2000);
        });
      }
      const maxReqNum = 3,
        requestList = [];
      for (let k = 0; k < 15; k++) {
        requestList.push(axios);
      }
      sendRequest(requestList, maxReqNum, function () {
        console.log("所有请求完成");
      });

按照请求顺序返回结果

要求:多个请求做并发限制,并且所有请求完成时,按照请求顺序返回结果

思路:

并发限制发送请求,需要发送的请求数量如果不够就一次完成了所有请求

每次完成一个请求就产生了一个空位,可以继续发送新的请求,通过索引来保证顺序

所有请求完成后(加了一个变量统计请求完成请求数量,并在finally中修改),按照请求发送的顺序返回所有请求的结果

      function requestLimit(urls, limit) {
        return new Promise((resolve) => {
          let index = 0; // 每次请求的索引
          // 存放所有请求的返回结果
          const resultList = [];
          if (urls.length === 0) return;
          const limit = Math.min(maxReqNum, urls.length); // 第一次发送多少个请求
          for (let i = 0; i < limit; i++) {
            request();
          }
          let finishedCount = 0; // 请求完成的数量
          // 发送请求
          function request() {
            const i = index; // i用来保证请求的顺序与相应顺序能对应上
            const url = urls[index];
            console.log(url);
            index++; // 每次发送一次请求加1,保证请求一个接一个
            axios(url)
              .then((res) => {
                resultList[i] = res + i;
              })
              .catch((err) => {
                resultList[i] = err + i;
              })
              .finally(() => {
                finishedCount++;
                if (finishedCount === urls.length) {
                  resolve(resultList); // 所有请求完成,修改promise状态
                }
                if (index < urls.length) {
                  request(); // 没发送完一个请求,只要还有请求没有完成就继续发请求
                }
              });
          }
        });
      }
      // 模仿接口请求
      function axios(url) {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            if (Math.random() > 0.6) {
              resolve("成功");
            } else {
              reject("失败");
            }
          }, Math.random() * 5000);
        });
      }

      // 测试一下,maxReqNum:最大并发数,urls:所有需要发送的请求
      const maxReqNum = 3,
        urls = [];
      for (let k = 0; k < 15; k++) {
        urls.push(`https:www.test.com/${k}`);
      }
      requestLimit(urls, maxReqNum)
        .then((res) => {
          console.log(res);
        })
        .catch((err) => {
          console.log(err);
        });

请求失败继续重新请求及重复请求次数限制

要求:多个请求做并发限制;当请求失败时,继续发送这个请求,直到请求成功或者达到了同一个请求重复请求次数的限制(请求算完成);所有请求完成时,按照请求顺序返回结果

思路:

在2的基础上;添加了一个failUrl来判断是不是继续重复请求失败的那个;如果重复的请求,就不会往正在请求的任务中添加新的任务,保证并发的限制;请求失败的时候,需要判断请求失败的次数,通过往请求的信息中添加了一个index(保证请求失败时拿到正确的对应重复请求信息)和count保证重复的请求达到限制就不再发送

      // 发送请求的方法
      function request(url) {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            if (Math.random() > 0.6) {
              resolve("成功");
            } else {
              reject("失败");
            }
          }, Math.random() * 2000);
        });
      }

      function requestLimitFunc(maxReqNum, urlList, sameLimit) {
        return new Promise((resolve) => {   
          let index = 0;  // 通过索引,从urlList取出请求
          const resultList = [];  // 保留请求结果的数组
          // 最多可以发多少个请求
          const limit = Math.min(maxReqNum, urlList.length);
          let finishCount = 0;  // 有多少个请求已完成
          function activeTask(params) {
            for (let i = 0; i < limit; i++) {
              excuteTask();
            }
          }

          function excuteTask(failUrl) {
            let reqUrl = "";
            let i = 0;
            if (!failUrl) {  
              i = index; // 如果传了failUrl,重新请求失败的那个,不需要往任务中添加请求了
              reqUrl = urlList[index].url;
              index++;
            } else { 
              i = failUrl.reqIndex;
              reqUrl = failUrl.url;
            }

            request(reqUrl)
              .then((res) => {
                finishCount++;
                resultList[i] = res;
                console.log(urlList[i].url, res);
                // 一个请求完成,需要添加新的请求进来
                if (index < urlList.length) excuteTask();
              })
              .catch((err) => {
                // 请求失败需要重新发送请求,直到请求成功或达到最多发送次数
                if (sameLimit === urlList[i].count) {
                  finishCount++;
                  resultList[i] = err;
                  console.log("失败了", urlList[i].url);
                  if (index < urlList.length) excuteTask();
                } else {
                  urlList[i].count++;
                  excuteTask(urlList[i]);
                }
              })
              .finally(() => {
                if (finishCount === urlList.length) resolve(resultList);
              });
          }
          activeTask();  // 第一次启动,在并发限制内发送最多的请求
        });
      }

      //   等待请求的数组
      const urlList = [];
      for (let i = 0; i < 15; i++) {
        urlList.push({
          reqIndex: i,
          count: 1,
          url: `https:www.test.com/${i}`,
        });
      }
      const maxReqNum = 5; // 最大并发数
      const sameLimit = 3; // 同一个请求失败重新请求,最多请求3次
      requestLimitFunc(maxReqNum, urlList, sameLimit).then((res) => {
        console.log(res);
      });

你可能感兴趣的:(网络请求,前端,javascript)