面试题:JS怎么实现并发控制任务执行呢?

1,049 阅读3分钟

本文正在参加「金石计划」

题目

给你一段如下代码,要求我们在调用addTask函数时,要求我们有多个任务队列,使我们可以同时执行多个函数。

例如:我们给addTask添加了五个任务,任务1需要 10s、任务2需要 5s、任务3需要3s、 任务4需要4s、 任务5需要5s。我们现在有两个任务队列去执行这个五个任务,需要在5s执行完任务2, 8s执行完任务3,10秒执行完任务1, 12s执行完任务4, 15s执行完任务5。

function timeout(time) {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve()
       }, time)
    })
}
const superTask = new SuperTask(2)
function addTask(time, name) {
    superTask
    .add(() => timeout(time))
    .then(() => {
        console.log(`任务${name}完成`);
    })
}
addTask(10000, 1)   // 10000 1
addTask(5000, 2)    // 5000 2  
addTask(3000, 3)    // 8000 3
addTask(4000, 4)    // 12000 4
addTask(5000, 5)    // 15000 5

思路

首先,我们先要明白这道题目的意思,利用JS中 promise 异步处理的思想,维护出两个任务队列,分别用两个任务队列同时执行任务1和任务2, 根据任务执行完的先后,改变各自任务的promise状态,执行.then中的回调函数。如果还有任务3,则看哪个任务先执行完,将任务3添加至任务与队列。依此类推,直到全部执行完。

关键步骤

如果我们想达到该题目的效果,有如下九步关键步骤:

1. 定义SuperTask类,接收任务队列的个数
2. 在SuperTask类中定义一个确定正在执行的任务个数
3. 在SuperTask类中定义一个任务队列来承装任务
4. 在SuperTask类中定义一个函数来向任务队列添加任务
5. 在SuperTask类中定义一个函数来执行任务队列的任务
6. 添加任务时,需要将任务和任务状态都添加到任务队列中(**同时我们还要返回出一个promise对象**)
7. 添加完任务后,需要调用执行任务函数执行在任务队列头部的任务,同时增加正在执行的任务个数
8. 当该任务执行完后,改变该任务的promise状态,同时减少正在执行的任务个数
9. 如果任务队列不为空,递归执行任务函数执行任务队列中的其他任务

具体实现

class SuperTask {  // 步骤一
    constructor(paralleCount = 2) {
      this.paralleCount = paralleCount;
      this.runningCount = 0; // 步骤二
      this.tasks = [];  // 步骤三
    }
    add(task) { // 步骤四
      return new Promise((resolve,reject) => { 
        this.tasks.push({ // 步骤六
          task,
          resolve,
          reject
        });
         this._run();   // 步骤七
      })
    }
    _run() { // 步骤五
      if(this.tasks.length && this.runningCount < this.paralleCount) { 
        this.runningCount++; 
        const { task,resolve,reject } = this.tasks.shift();
        task().then(resolve,reject).finally(res => { // 步骤八 
          this.runningCount--; 
          this._run(); // 步骤九
        })
      }
    }
  }

效果:

图片.png

由此,我们就完整的写出了一个可以控制JS并发任务执行的类,同时我们还可以控制任务队列的个数。

总结

不难看出,我们实现js并发控制任务执行就是很巧妙的借助了利用Promise.then微任务的特性,通过控制promise状态的改变, 来控制.then中回调函数执行的时间,同时借助promise异步执行的特性同时执行多个任务。