如何优雅的处理并发任务

289 阅读2分钟

在前端的日常开发中,经常会碰到并发任务的处理。在有了promise后,处理并发任务变得简单。那如何优雅的处理并发任务呢?首先我们需要先分析一下并发任务的执行过程。

如上图,假设有10个待执行的任务,我们需要同时执行3个并发,nextIndex为任务下标。程序开始,任务1-3同时开始执行(调用任务函数),任务4-10等待执行。当任务1-3有任务执行完成,就可以继续执行等待中的任务。

当任务数大于并发数

假设10个任务,3个并发,任务的初始下标nextIndex=0

  1. 执行任务1,nextIndex=1
  2. 执行任务2,nextIndex=2
  3. 执行任务3,nextIndex=3

设任务1、2、3按顺序执行完成:

  1. 任务1优先完成,此时nextIndex=3(执行任务3的时候被设置为3),任务4进入执行阶段,nextIndex=4
  2. 任务2执行完成时,此时nextIndex在执行任务4的时候被更改为4,所以任务2执行完后,任务5进入执行阶段,nextIndex=5
  3. 任务3执行完成时,此时nextIndex在执行任务5的时候被更改为5,所以任务3执行完后,任务6进入执行阶段,nextIndex=6

当任务数小于并发数

当任务数小于并发数的时候,则我们只需要同时执行所有任务数即可

任务什么时候完成

我们定义一个finishCount,每完成一个任务时,我们需要将finishCount加1,当finishCount等于任务数时,则任务完成,返回最终结果

具体代码如下

/**
 * 并发执行任务
 * @param {Function[]} tasks
 * @param {Number} paralleCount
 * @returns {Promise<unknown>}
 */
function paralleTask(tasks, paralleCount = 2) {
    return new Promise(resolve => {
        //特殊情况
        if (tasks.length === 0) {
            resolve();
            return;
        }
        let nextIndex = 0;//记录下一个任务的下标
        let finishCount=0;//任务完成数
        function _run() {
            //运行下一个任务
            const task = tasks[nextIndex];
            nextIndex++;
            task().then(() => {
                finishCount+=1;
                //还有下一个任务,运行下一个
                if (nextIndex < tasks.length) {
                    _run();
                }else if(finishCount===task.length){//所有任务完成时机
                    resolve()
                }
            })
        }
      	// 并发执行任务,并判断任务数小于并发数
        for (let i = 0; i < paralleCount && i < tasks.length; i++) {
            _run();
        }
    })
}

const tasks = [];
paralleTask(tasks, 4).then(() => {
    console.log('全部完成');
})