如何实现控制请求并发数量

191 阅读2分钟

背景

无论是上传大文件还是批量上传图片的时候我们都需要去控制并发的请求数量,限制并发请求数量可以减少网络延迟和响应时间,提高系统的整体性能。当请求并发量过高时,服务器可能会变得不稳定,响应时间会增加。通过控制并发量,可以避免这些问题并提供更好的用户体验。

实现

// 异步函数
function timeout(val) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve();
    }, val);
  });
}

// 定义并发异步队列
class SuperTask {
  constructor() {
    this.maxCount = 2; // 异步并发任务
    this.progressTaskNum = 0;
    this.taskList = [];
  }
  add(fn) {
    return new Promise((resolve, reject) => {
      const storeFn = {
        fn,
        resolve,
        reject,
      };
      this.taskList.push(storeFn);
      this.run();
    });
  }
  run() {
    if (this.taskList.length > 0 && this.progressTaskNum < this.maxCount) {
      const { resolve, reject, fn } = this.taskList.shift();
      this.progressTaskNum++;
      return fn()
        .then(resolve, reject)
        .finally(() => {
          this.progressTaskNum--; // 递归调用下一个队列任务
          this.run();
        });
    }
  }
}
const superTask = new SuperTask();

function addTask(time, name) {
  superTask
    .add(() => timeout(time))
    .then(() => {
      console.log(`任务${name}完成}`);
    });
}

// 返回结果会1s后返回任务1&2,再过1s返回2&3
// 任务队列会根据请求的快慢依次保持最大并发数
addTask(1000, "任务1");
addTask(1000, "任务2");
addTask(1000, "任务3");
addTask(1000, "任务4");

优化调用

上面的调用方式肯定不适用在项目里面,所以我需要改变调用的方法和逻辑,在初始化的时候就安排上并发任务队列,类似于Promise.all([...])的方式调用请求返回promise,得到异步返回的数组。

function timeout(val) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('返回数据');
    }, val);
  });
}
class SuperTask {
  constructor(maxCount) {
    this.maxCount = maxCount; // 异步并发任务
    this.progressTaskNum = 0;
    this.taskList = [];
    this.response = [];
  }
  init(reqList = []) {
    for (let index = 0; index < reqList.length; index++) {
      const request = reqList[index];
      this.add(()=>request)
    }
  }
  add(fn) {
    return new Promise((resolve, reject) => {
      const storeFn = {
        fn,
        resolve,
        reject,
      };
      this.taskList.push(storeFn);
      this.run();
    });
  }
  run() {
    if (this.taskList.length > 0 && this.progressTaskNum < this.maxCount) {
      const { resolve, reject, fn } = this.taskList.shift();
      this.progressTaskNum++;
      return fn()
        .then(res=>{
          resolve(res)
          this.response.push(res)
        })
        .catch(err=>{
          reject(err)
        })
        .finally(() => {
          this.progressTaskNum--; // 递归调用下一个队列任务
          this.run();
        });
    }
  }
}
const superTask = new SuperTask(2);
// 通过初始化请求队列,降低使用负担
// 结果也可以通过response拿到一个返回数组[异步返回]superTask.response
superTask.init([timeout(1000), timeout(2000), timeout(3000)])

promise A+

如何去深入理解promise,最好的方式就是自己实现一个基础的promise,让自己更深刻的理解这个api,当然面试的时候面试官也会让你手写或者说下实现思路,所以深入了解还是挺有必要。

参考地址

// promise a+实现本地测试
npm install promises-aplus-tests -g
promises-aplus-tests my-promise.js
// 实现代码后面添加如下代码即可
MyPromise.defer = MyPromise.deferred = function () {
  let dfd = {};
  dfd.promise = new MyPromise((resolve, reject) => {
      dfd.resolve = resolve;
      dfd.reject = reject;
  });
  return dfd;
};

module.exports = MyPromise