前端性能优化——控制并发

384 阅读3分钟

前言

性能优化这一块在前端是一个比较重要并且常考的知识,小编最近在面试的过程中也是被面试官问到了如何处理,比如前端现在有100个请求需要发送给后端,一次性全部发送必然会造成服务器的压力很大,

所以此时我们需要控制并发数量,每次只能发送5个请求,面试官问你这种场景你会如何实现?

正文

设计思路

比如把这十个任务全部装到任务队列里,然后一次性把队列里的前两个取出来运行即可,取到两个后就不在取了,等到某个任务运行结束了之后我们再去递归一下取任务的这个行为,取出任务来去运行

这里我们可以考虑使用队列去请求大量接口。

思路如下:

假定最大并发数是paralleCount=5,我们对接口进行了定义编号,当请求队列池中有一个请求返回后,就向池子中新增一个接口进行请求,依次直到最后一个请求执行完毕。

为了模拟发送起步请求这一操作,这里咱们通过定时器定义一个timeout函数

来实现,传入一个时间表示请求耗时。

function timeout(time){
	return new Promise((resolve,reject)=>{
		setTimeout(()=>{
			resolve();
		},time)
	})
}

接下来咱们定义一个控制并发的类来专门来实现控制并发这么一个功能,构造函数可以接受一个参数来表示每次的并发数量。里面有一个任务数组tasks用来存储异步任务,还有一个变量runningCount表示正在运行的任务。

class SuperTask{
	constructor(paralleCount = 2){
		this.paralleCount = paralleCount;//并发量
        this.tasks = []
        this.runningCount = 0;//正在运行的任务量
	}
}

接着咱们还需要再里面定义一个add方法来添加异步任务,这个add会返回一个Promise.

const superTask = new SuperTask();
superTask.add(()=>timeout(time))
    .then(()=>console.log(`任务完成`))

add(task){//添加任务
        return new Promise( (resolve,reject)=>{
            this.tasks.push({//在数组里面添加异步任务
                task,resolve,reject
            })
        this._run();//这里的this指向的是实例对象
        })
    }

然后当add方法接收到异步任务之后会将任务放进任务队列中,关键在于如何去触发这些任务并且控制并发数量为5呢?这个_run方法就是用来依次运行任务的。

_run(){ //依次运行任务
        while(this.runningCount<this.paralleCount&&this.tasks.length){
            const {task,resolve,reject} = this.tasks.shift();
            this.runningCount++;
            task().then(resolve,reject).finally(()=>{ //不管任务成功还是失败,任务执行完毕
                this.runningCount--;
                this._run();
            })
        }
    }

这里咱们通过判断任务数组中有任务并且当前正在运行的任务数小于并发数时,将任务队列的任务取出并且运行他,不管任务成功还是失败,任务执行完毕都要将当前并发数减一,并且递归调用run方法。这样我们就实现了控制并发的这么一个效果。

最后咱们来测试一下效果

const superTask = new SuperTask();
function addTask(time,name){
    superTask.add(()=>timeout(time))
    .then(()=>console.log(`任务${name}完成`))
}
addTask(10000,1)
addTask(2000,2)
addTask(1000,3)
addTask(1000,4)
addTask(3000,5)
addTask(2000,6)
addTask(4000,7)
addTask(2000,8)

image-20240916152418650

完美!

最后附上全部的代码:

function timeout(time) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, time);
  });
}
// timeout(1000)
// timeout(1000)
// timeout(1000)
// ...
class SuperTask {
  constructor(paralleCount = 2) {
    this.paralleCount = paralleCount; // 并发量
    this.tasks = [];
    this.runningCount = 0; // 正在运行的任务量
  }
  add(task) {
    return new Promise((resolve, reject) => {
      this.tasks.push({
        // 10
        task,
        resolve,
        reject,
      });

      this._run();
    });
  }
  _run() {
    // 依次运行tasks中的任务
    while (this.runningCount < this.paralleCount && this.tasks.length) {
      const { task, resolve, reject } = this.tasks.shift();
      this.runningCount++;
      task()
        .then(resolve, reject)
        .finally(() => {
          this.runningCount--;
          this._run();
        });
    }
  }
}
const superTask = new SuperTask();
function addTask(time, name) {
  superTask
    .add(() => timeout(time))
    .then(() => {
      console.log(`任务${name}完成`);
    });
}
addTask(10000, 1);
addTask(2000, 2);
addTask(5000, 3);
addTask(1000, 4);
addTask(7000, 5);
addTask(3000, 6);