如何实现一个控制并发的任务管理器

541 阅读4分钟

前言

有这样一个场景:前端一次性有100请求要发出去,但是后端一次性只能处理50个请求,这时你会怎么办呢? 在日常开发中,我们经常会遇到需要同时处理多个异步任务的情况,然而,如果同时启动过多的任务会导致资源耗尽或影响性能,一旦任务多了很有可能导致服务器崩溃,因此,合理地控制并发数量是很重要的。本文将介绍如何实现一个简单的并发任务管理器。

正文

延时函数

首先,我们写一个简单的延时函数,它返回一个Promise,以便我们可以使用它来模拟异步任务。

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

这里,timeout 函数接收一个参数 time,表示延时的时间。

创建任务管理器类

接下来,我们定义一个名为 SuperTask 的类,它负责管理并发的任务队列。

class SuperTask {
    constructor(paralleCount = 2) {
        // 设置并发任务的最大数量,默认为 2。
        this.paralleCount = paralleCount;
        // 初始化一个空数组,用于存储待处理的任务。
        this.tasks = [];
        // 初始化正在运行的任务数量。
        this.runningCount = 0;
    }

    add(task) {
        // 添加一个新的任务到任务列表中。
        return new Promise((resolve, reject) => {
            // 将任务对象推入任务队列。
            this.tasks.push({
                // 任务本身
                task,
                // 当任务完成时调用的函数
                resolve,
                // 当任务失败时调用的函数
                reject
            });
            // 在添加任务之后,尝试运行任务队列。
            this._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();
            });
        }
    }
}

代码解释:

  1. 构造函数constructor 方法初始化了 SuperTask 类的实例,设置了并发任务的最大数量(默认为 2),并初始化了两个数组:tasks 用于存储等待执行的任务,runningCount 用于记录当前正在运行的任务数量。
  2. 添加任务add 方法接收一个任务作为参数,并将其封装成一个任务对象,包含任务函数、成功回调和失败回调,然后将这个任务对象添加到任务队列中,并尝试运行任务队列。
  3. 运行任务_run 方法用于管理任务的执行,它会检查当前正在运行的任务数量是否小于最大并发数,并且任务队列中还有任务,如果条件满足,则从任务队列中取出一个任务,增加正在运行的任务数量,并执行该任务。任务执行完毕后,减少正在运行的任务数量,并再次尝试运行任务队列。

使用任务管理器

最后,我们创建一个 SuperTask 实例,并添加一些任务来看看它是如何工作的。

const superTask = new SuperTask();

function addTask(time, name) {
    // 向任务管理器添加一个任务。
    superTask
        .add(() => timeout(time))  // 添加一个延时任务。
        .then(() => console.log('任务完成', name));  // 任务完成后打印一条消息。
}

// 添加几个不同时间的任务。
addTask(10000, 1);
addTask(2000, 2);
addTask(3000, 3);
addTask(3000, 4);
addTask(6000, 5);

代码解释

  1. 创建任务管理器实例superTaskSuperTask 类的一个实例,我们可以通过它来管理任务。
  2. 添加任务函数addTask 函数接收两个参数:time 表示任务的延时时间,name 用于标识任务。它调用 superTask.add 方法添加一个任务。
  3. 添加多个任务:我们添加了五个不同的任务,每个任务有不同的延时时间。

效果:

0e812bc928fa5896d1876f7f7c4d9ea6.gif

通过这个简单的并发任务管理器,我们可以有效地控制同时运行的任务数量,避免资源过度消耗,并确保所有任务都能按照预期顺序执行。这对于处理大量异步操作特别有用。

完整代码

最后完整代码奉上:

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

class SuperTask{
    constructor(paralleCount=2){  //不传就是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(3000,3)
addTask(3000,4)
addTask(6000,5)

总结

本文到此就结束了,希望对你有所帮助,感谢你的阅读!