编程练习:阻塞(1)异步调度

193 阅读2分钟

实现一个Scheduler类,使下面的代码能正确输出。

const timeout = (time, value) => new Promise(resolve => {
  setTimeout(() => resolve(value), time)
})
const scheduler = new Scheduler(2) 
const addTask = (time, order, value) => {
  return scheduler.add(() => timeout(time, value))
    .then((value) => {
      console.log(order)
      return value;
    })
}

addTask(1000, '1')
addTask(500, '2')
addTask(300, '3')
addTask(400, '4')
// output: 2 3 1 4

解题思路

  1. 观察题目
  • timeout 传递时间和值两个参数,返回一个promise,promise中有一个执行函数,一定时间之后执行resolve。
  • scheduler 通过 new Schedule 产生一个调度的对象。
  • addTask 传递 时间、编号、返回值三个参数,返回调度对象.add 方法执行的结果。 add对象传递 timeout函数进去,并且答应序号和 timeout函数传递的值。
  • 执行顺序是 1、2、3、4 最后的结果是 2、3、1、4
  1. 推测
  • 根据执行的顺序和结果的顺序推测,构造函数里传入的是 限制最多同时执行任务的数量。

1(1000)、2(500)同时执行,2(500)先执行完毕,然后执行 3(300),3执行完毕后,执行4(400),然后1执行完毕,最后4执行完毕。

  1. 伪代码
class Scheduler {
    constructor(最大任务数) {
        维护一个队列存放不立即执行的任务。
        维护当前执行的任务数
        维护最大任务数
    }
    add(任务) {
        if 当前任务数 > 最大任务数 就用await阻塞住,
        push resolve到维护的数组里,执行完毕后解除阻塞。
        
        任务数+1  执行任务 执行任务(用await阻塞,任务执行完毕后才解除阻塞) 
        任务数-1 检查数组如果有不立即执行的任务就吐出来。
    }
}
  1. 答案
class Scheduler {
    // 构造函数
    constructor(maxJob) {
        this.maxJob = maxJob; // 最大任务数
        this.queue = []; // 维护的队列
        this.currentJob = 0; // 当前任务数
    }
    async add(callback) {
        // 当前任务数大于最大任务数时阻塞,否则执行
        if(currentJob > maxJob) {
            await new Promise(resolve => this.queue.push(resolve))
        } 
        this.currentJob++;
        const result = await callback();
        this.currentJob--;
        if(queue.length !== 0) {
            // 从队列里取出先push进的 resolve 执行取消阻塞
            this.queue.unshift()();
        }
        return result;
    }
}