js控制promise的最大并发数量

594 阅读1分钟

问题:我们在前端请求中有时会碰到大量的并发请求下载,promise.all可以做到并发请求,但是没有对最大的并发数量做控制。

解决方案:

module.exports = class myAsyncPoll {
  list = []; //任务池
  maxLimit = 50; //任务池最大同时执行任务队列的数量
  workingNum = 0; //任务池中启动队列的数量

  //设置最大执行数量
  setMaxLimit(num) {
    this.maxLimit = num;
  }

  //往队列池中添加任务
  add(promiseCreater) {
    this.list.push(promiseCreater);
  }

  //开始启动任务池中的任务队列
  start() {
    for (let i = 0; i < this.maxLimit; i++) {
      this.doNext();
    }
  }

  //任务队列的自动执行(空闲自动调用下一个)
  doNext() {
    if (this.list.length && this.workingNum <= this.maxLimit) {
      this.workingNum++;
      //this.list.shift()() 找到数组中第一个并调用异步任务
      //.then()中执行下一个,并修改工作的任务数量-1
      this.list
        .shift()()
        .then(() => {
          this.workingNum--;
          this.doNext();
        });
    }
  }
};

思路:我们定义一个任务池,通过add方法往任务池中添加(promise对象),通过doNext()方法,做到任务池中的每个任务队列都能执行完每个任务以后,将空闲的任务自动补充进队列中。通过start()开启多条,setMaxLimit()方法设置的最大任务队列。

举一个栗子:(productsInfo是保存了图片下载链接的数组)

   const asyncPool = new myAsyncPoll();
   asyncPool.setMaxLimit(maxDownloadNUM); //设置最大同时并发任务量
   productsInfo.map(async (item) => {
     asyncPool.add(() => {
       return new Promise((resolve) => {
         downloadFile(
           item.imgUrl,
           `./public/${currentPage}`,
           `${item.title.replace(/\/|\\|\*|"|'/g, "-")}.png`
         );
       });
     });
   });
   asyncPool.start();

downloadFile:

   const axios = require("axios");
   const fs = require("fs");
   const path = require("path");

   module.exports = async function downloadFile(url, filepath, name) {
     try {
       if (!fs.existsSync(filepath)) {
         fs.mkdirSync(filepath);
       }
       const mypath = path.resolve(filepath, name);
       const writer = fs.createWriteStream(mypath);
       const response = await axios({
         url,
         method: "GET",
         responseType: "stream",
       });
       response.data.pipe(writer);
     } catch (error) {
       console.log(error);
     }
   };