JS实现并发任务调度(面试题)

890 阅读2分钟

题目内容

任务调度,实现一定任务的并发(面试的时候要求的2个任务)

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

基础知识

这个题需要的基础知识相对比较简单

  • 构造函数
  • Promsie

题目分析

  • 从使用方法可知,SuperTask是一个构造函数。
  • SuperTaskadd的方法,可以链式调用then,那么返回值是promise
  • 因为是固定并发数量,所以SuperTask中需要有属性保存最大并发数,以及当前正在执行的任务数量。
  • addTask可以不断被调用,但是我们并发是固定的,所以SuperTask中需要有属性保存总的任务。
  • 我们保存任务的时候,不能让task执行,所以需要封装一下,保证我们执行任务的时候再执行内部的内容。

实现

function SuperTask(maxTaskNumber = 2) {
    // 保存需要执行的任务
    this.tasks = [];
    
    // 正在执行的任务数量
    this.runTaskNumber = 0;
    
    // 最大的同时(并发)执行的任务数量
    this.maxTaskNumber = maxTaskNumber;
}
SuperTask.prototype.add = function(fn) {

    // 根据使用方法,推测出add函数返回的是一个Promise对象
    return new Promise(resolve => {
    
        // 处理任务,任务执行完成后,需要改变 add 返回的promise的状态
        // 保证任务执行完成后,触发add函数后的 then 方法
        const task = () => {
        
            // 从题目可知fn的返回值时promise对象
            // 因为我们需要根据task执行完成后,再执行后续任务,所以这儿需要把fn的执行结果返回
            return fn.call(null).then(() => {
                resolve();
            })
        }
        
        // 保存任务
        this.tasks.push(task);
        
        // 执行任务
        this.run();
    })

}
SuperTask.prototype.run = function() {
    
    // 当前正在执行的任务 小于 并发数量 且 还有需要执行的任务时
    while(this.runTaskNumber < this.maxTaskNumber && this.tasks.length) {
        
        // 正在执行的任务数+1
        this.runTaskNumber++;
        
        // 获取当前最先被保存在总任务中的任务
        const task = this.tasks.shift();
        
        task().then(() => {
        
            // 任务执行完成后,正在执行的任务 -1
            this.runTaskNumber--;
            
            // 开始下一轮的执行任务
            this.run();
        })
    }
}