如何基于JS实现Ajax并发请求的控制详解

目录
  • 前言
  • Ajax的串行与并行
  • Ajax的并发请求控制的两大解决方案
    • 基于Promise递归实现
    • 基于Class实现
  • 代码展示
  • 总结

前言

最近看到一个面试题,当然了,就是这篇文章的标题,Ajax的并发请求的控制,感觉挺有意思的,在社区看了下,应该是字节的面试题,也挺多大佬对这个进行了总结,都看了下,于是自己也想试着总结下,代码文末会全部贴出,如有不足,请指出!

Ajax的串行与并行

  • 串行:一般业务需求是下个接口需要用到上个接口的返回的数据,前端常用的请求库是Axios,本身就是基于Promise的HTTP库,我们直接采用链式调用,或者采用Async Await 的方式就可以实现,就不做演示了
  • 并行:就是多个请求同时发生,一般情况会用于当所有数据都拿到后进行渲染页面,或者其他的操作,主要还是基于Promise.all实现方式如下

上面模拟实现了基于Promise.all的并行操作,打印结果入下

是不是觉得的这就结束了,不存在的?接下来才是正菜,我们试想有种情况,一个页面中需要同时发送上万个请求,全部成功后再去做一些操作,这样会导致什么后果呢?代码无法执行,内存溢出,哎~这个时候就回到了我们文章的主题上了,如何对Ajax并发请求的控制呢?我让他一次性只能输出一定数量的请求,直到所有的都成功为止,接下来我将用两种方法实现这个操作,望读者朋友们不吝赐教

Ajax的并发请求控制的两大解决方案

基于Promise递归实现

以下的两种方法都是需要用到上图中的那个Tasks数组的,请大家记住它,第一中基于Promise的方法,大致思路是:当你传入一个并发量pool时,会创建pool个工作区,每一个工作区回去拿对应的任务(请求)去执行,成功后保存,然后继续去拿任务(请求),直到工作区没有任务了,当然了,失败直接终止, 大致思路就是这样,下面我对每一行代码进行了注释,请笑纳

基于Class实现

第二种方法是基于Class来实现的,和上面的区别在于这个只创造一个工作区,大致思路:创建一个工作区用于执行任务(请求),然后将所有任务都推入,但是没次只能执行对应的并发数,当小于并发数市,继续去拿任务执行它,直到没有任务(请求)为止, 就是这样,下面是具体实现

代码展示

这里把这两种方法的实现代码贴出

    const delay = function delay(interval) {
      return new Promise((res,rej) => {
        setTimeout(() => {
          res(interval)
        }, interval);
      })
    }

    let tasks = [() => {
      return delay(1000)
    },() => {
      return delay(1003)
    },() => {
      return delay(1005)
    },() => {
      return delay(1002)
    },() => {
      return delay(1004)
    },() => {
      return delay(1006)
    }]

    // 通过Promise.all实现并行
    Promise.all(tasks.map(task => task())).then(res => {
      console.log(res);
    })

    // 基于Promise实现

    function creatRequest(tasks,pool) {
      // 每次控制的发送请求的数量pool
      pool = pool || 5
      // 用于存储每一次请求的结果(按顺序进行存贮)
      let results = [],
      // together 用于创建工作区,当pool传入的是几,我们就对应的创建几个工作区
      // 也就是创建一个长度为pool且值为null的一个数组
          together = new Array(pool).fill(null),
      // index为每次获取的任务值
          index = 0;
      together = together.map(() => {
        // 基于Promise进行管理
        return new Promise((resolve,reject) => {
          // 创建一个函数,进来立刻执行
          const run = function run() {
            // 如果任务池已经空了,说明请求发送完成了,直接成功
            if(index >= tasks.length) {
              resolve()
              return
            }
            // 先将index保存一下用于存储当前成功请求的结果
            let old_index = index,
            // 获取当前发送的请求,然后把index进行累加,所以上面会把index保存起来
            // 这里index++ 是先运算后累加的,而++index则相反,先累加后运算
                task = tasks[index++];
            // 执行请求
            task().then(result => {
            // 将成功结果保存
              results[old_index] = result
            // 递归继续执行,也就是继续拿到任务到工作区执行
              run();
            }).catch(reason => {
              reject(reason)
            })
          }
          // 立即执行
          run()
        })
      })
      // 用Promise.all管控工作区,也就是每次并发两个请求
      return Promise.all(together).then(() => results)
    }

    creatRequest(tasks,2).then(results => {
      // 都成功,整体才成功,按顺序存储结果
      console.log('成功',results);
    }).catch(resolve => {
      // 只要有一个失败,整体失败
      console.log('失败');
    })

    // 基于Class实现
    function creatRequest(tasks,pool,callback) {
      // 参数的限制与验证
      if(typeof pool === 'function') {
        callback = pool;
        pool = 5
      }
      if(typeof pool !== 'number') pool = 5
      if(typeof callback !== 'function') callback = function () {}
      // -------
      class TaskQueue {
        // 正在运行的个数
        runing = 0;
        // 将所有任务所存在的队列
        queue = [];
        // 存储执行任务(请求)的结果
        results = [];
        pushTask(task) {
          let self = this
          // 将任务推入工作区
          self.queue.push(task)
          // 执行发送请求的逻辑
          self.next()
        }
        next() {
          let self = this
          // 当正在执行的任务数小于并发量的时候继续去拿任务执行
          while(self.runing < pool && self.queue.length) {
            self.runing++;
            // 相当于拿一个任务删除一个任务
            let task = self.queue.shift();
            // 执行请求
            task().then(result => {
              // 将执行结果保存
              self.results.push(result)
            }).finally(() => {
              // 将正在运行的个数清除
              self.runing--
              // 继续执行请求
              self.next()
            })
          }
          // 当没有任务了循环结束
          if(self.runing === 0) callback(self.results)
        }

      }
      // 实例化
      let TQ = new TaskQueue()
      tasks.forEach(task => TQ.pushTask(task))
    }

    creatRequest(tasks,2,results=> {
      console.log(results);
    })

总结

以上就是对这套面试题的总结了,也是自己做下记录,后续会不断的更新前端方面的文章,最后还是希望各位前端的小伙伴们都能坚持学习,技术不断提升,加油吧,骚年!!!

到此这篇关于如何基于JS实现Ajax并发请求控制的文章就介绍到这了,更多相关JS实现Ajax并发请求控制内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 字节跳动面试之如何用JS实现Ajax并发请求控制

    前言 讲真的,最近也很迷茫.关于技术.关于生活吧.也找了很多在大厂的朋友去聊,想需求一些后期发展的思路.这其中也聊到了面试,聊到了招聘中会给面试者出的一些题目.我正好也好久没面试了,就从中选了几道.最近也会陆续出一系列关于一些面试问题的解析. 今天这道是字节跳动的: 实现一个批量请求函数 multiRequest(urls, maxNum),要求如下: • 要求最大并发数 maxNum • 每当有一个请求返回,就留下一个空位,可以增加新的请求 • 所有请求完成后,结果按照 urls 里面的顺序依

  • 利用js实现Ajax并发请求限制请求数量的示例代码

    出现问题描述:当不确定异步请求个数时,为防止当一瞬间发生上百个http请求时,导致堆积了无数调用栈进而导致内存溢出问题. 要求:将同一时刻并发请求数量控制在3个以内,同时还要尽可能快速的拿到响应的结果. 同面试问题: 实现一个批量请求函数 multiRequest(urls, maxNum),要求如下: 要求最大并发数 maxNum 每当有一个请求返回,就留下一个空位,可以增加新的请求 所有请求完成后,结果按照 urls 里面的顺序依次打出 1.基于Promise.all实现Ajax的串行和并行

  • 如何基于JS实现Ajax并发请求的控制详解

    目录 前言 Ajax的串行与并行 Ajax的并发请求控制的两大解决方案 基于Promise递归实现 基于Class实现 代码展示 总结 前言 最近看到一个面试题,当然了,就是这篇文章的标题,Ajax的并发请求的控制,感觉挺有意思的,在社区看了下,应该是字节的面试题,也挺多大佬对这个进行了总结,都看了下,于是自己也想试着总结下,代码文末会全部贴出,如有不足,请指出! Ajax的串行与并行 串行:一般业务需求是下个接口需要用到上个接口的返回的数据,前端常用的请求库是Axios,本身就是基于Promi

  • 基于js中this和event 的区别(详解)

    今天在看javascript入门经典-事件一章中看到了 this 和 event 两种传参形式.因为作为一个初级的前端开发人员平时只用过 this传参,so很想弄清楚,this和event的区别是什么,什么情况下用什么比较合适. onclick = changeImg(this)       vs     onclick = changeImg(event) <img src='usa.gif' onclick="changeImg(event)" /> <scrip

  • 基于js的变量提升和函数提升(详解)

    一.变量提升 在ES6之前,JavaScript没有块级作用域(一对花括号{}即为一个块级作用域),只有全局作用域和函数作用域.变量提升即将变量声明提升到它所在作用域的最开始的部分. 上个简历的例子如: console.log(global); // undefined var global = 'global'; console.log(global); // global function fn () { console.log(a); // undefined var a = 'aaa';

  • js原生Ajax的封装和原理详解

    原理及概念 AJAX即"Asynchronous Javascript And XML"(异步JavaScript和XML),是一种用于创建快速动态网页的技术. 动态网页:是指可以通过服务器语言结合数据库随时修改数据的网页. 静态网页,随着html代码的生成,页面的内容和显示效果就基本上不会发生变化了--除非你修改页面代码. AJAX = 异步 JavaScript和XML(标准通用标记语言的子集). AJAX 是与服务器交换数据并更新部分网页的艺术,在不重新加载整个页面的情况下. A

  • Node.js中的HTTP请求与响应详解

    在C#.OC中也是客户端发起一个请求,服务端作出响应.我们可以把这个过程抽象理解 . 1.客户端给服务端发起请求相当于向服务端写入一个流(writable) 2.服务端读取客户端的流(readable) 3.服务端向客户端作出响应相当于向客户端写入一个流(writable) 4.客户端读取服务端的响应(readable) 整个流程分为两部分一是客户端的处理而是服务端的处理.最主要的还是客户端请求和服务端响应. 一.http.ClientRequest对象 它实现了writable流,可以使用它的

  • 原生JS写Ajax的请求函数功能

    一般我们写网页的时候,如果用到 Ajax 请求服务器,都是使用 JQuery 等已经封装好的库来调用,比较简单. 但是一般这些库的功能很多,引入了太多我们用不到的东西,如果我们需要写一个功能单一,简单的页面,完全用不到引用如此庞大的库文件. 我们可以简单实现一个自己的 Ajax 请求功能,具体的代码如下: var ajax = {}; ajax.x = function () { if (typeof XMLHttpRequest !== 'undefined') { return new XM

  • 基于Axios 常用的请求方法别名(详解)

    Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中. 常用的请求方法别名一般有: Get/post/http协议请求 执行Get请求 function get(){ return axios.get('/data.json', { params:{ id:1234 } }).then(function (response) { console.log(response); }) .catch(function (error) { console.log

  • 基于线程、并发的基本概念(详解)

    什么是线程? 提到"线程"总免不了要和"进程"做比较,而我认为在Java并发编程中混淆的不是"线程"和"进程"的区别,而是"任务(Task)".进程是表示资源分配的基本单位.而线程则是进程中执行运算的最小单位,即执行处理机调度的基本单位.关于"线程"和"进程"的区别耳熟能详,说来说去就一句话:通常来讲一个程序有一个进程,而一个进程可以有多个线程. 但是"任务

随机推荐