执行队列问题?

    class Scheduler {
        waiting = []
        running = 0;

        add(promiseCreator) {
            return new Promise(resolve => {

                if (this.running >= 2) {
                    this.waiting.push(promiseCreator)
                }
                else {
                    this.running++;
                    promiseCreator().then(() => {
                        this.running--;
                        if (this.waiting.length) {

                            this.add(this.waiting.shift())
                        }

                        resolve();
                    })
                }

            })
        }
    }

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

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

题目的场景是实现一个只允许同一时间内最多存在两个执行队列,期间插入的会进入等待队列,然后空闲时间执行。
只允许修改Scheduler里面的内容

现在能执行前两个task,后面两个回调没执行是什么原因

阅读 647
avatarAI BotBETA

这个问题的主要原因是,当等待队列(waiting)中的任务被添加到执行队列(running)时,没有正确地增加 running 的计数。因此,尽管新的任务被添加到 waiting 队列并在适当的时候被移入 running 队列,但 running 的计数并没有增加,这导致一些任务可能无法执行。

你的 add 方法应该如下所示:

class Scheduler {
    waiting = []
    running = 0;

    add(promiseCreator) {
        return new Promise(resolve => {

            if (this.running >= 2) {
                this.waiting.push(promiseCreator);
            }
            else {
                this.running++;
                promiseCreator().then(() => {
                    this.running--;
                    if (this.waiting.length) {
                        this.add(this.waiting.shift());
                    }
                    resolve();
                });
            }

        });
    }
}

在这个修改版本中,当一个任务完成并且有等待队列中的任务需要执行时,我们使用 add 方法将等待队列中的第一个任务添加到执行队列中,并递归调用这个方法。这确保了只要有两个任务正在执行,就会从等待队列中取出新的任务添加到执行队列中。

4 个回答

调度似乎没有问题,问题在于可测试性不高或者说测试方案不正确:
if(this.running >= 2)分支里面,并没有去处理resolve
这样就会导致进入这个分支之后,对应的Promise永远没有resolve的机会,那么你试图在add(...).then里面执行的检测代码console.log(order)不可能被执行

另外可以把函数的职责稍加分离,面试的时候图个卷面整洁:

class Scheduler {
  watting = [];
  MAX_TASK_NUMBER = 2;
  running = 0;

  add(promiseCreator, params = [], context = null){
    return new Promise((resolver) => {
      this.watting.push([promiseCreator, params, context, resolver]);
      this.#checkTasks();
    })
  }

  #checkTasks(){
    const {MAX_TASK_NUMBER, running} = this;
    if(running >= MAX_TASK_NUMBER){
      return
    }
    this.#nextTask();
  }

  async #nextTask(){
    const { watting } = this;
    if(!watting.length){
      return
    }
    const [task, params, context, callback] = watting.shift();
    this.running++;
    const result = await task.apply(context, params);
    this.running--;
    this.#checkTasks();

    if(callback){
      callback(result);
    }
  }
}

确保每次从等待队列启动任务时都会处理它的结果,将resolve函数存储在数组中,当任务完成时调用该函数。
修复后的Scheduler类:

class Scheduler {
    waiting = [];
    running = 0;
    resolvers = []; // 用于存储resolve函数

    add(promiseCreator) {
        return new Promise((resolve) => {
            const task = () => {
                this.running++;
                promiseCreator().then(() => {
                    this.running--;
                    resolve(); // 当前任务完成,通知外部调用
                    if (this.waiting.length) {
                        let next = this.waiting.shift();
                        let nextResolve = this.resolvers.shift(); // 从resolvers数组中获取resolve函数
                        next().then(() => {
                            this.running--;
                            nextResolve();
                            if (this.waiting.length) {
                                this.add(this.waiting.shift());
                            }
                        });
                    }
                });
            };
            if (this.running >= 2) {
                this.waiting.push(promiseCreator);
                this.resolvers.push(resolve); // 将resolve函数存储在resolvers数组中
            } else {
                task();
            }
        });
    }
}
class Scheduler {
        waiting = []
        running = 0;

        add(promiseCreator) {
            return new Promise(resolve => {

                if (this.running >= 2) {
                    this.waiting.push(resolve)//得到各位大佬的启发,这里应该是存入resolve方法而不是传入promiseCreator本体
                }
                else {
                    this.running++;
                    promiseCreator().then(() => {
                        resolve();
                        this.running--;
                        if (this.waiting.length) {
                            this.waiting.shift()()//这里提取出等待队列之后直接执行就可以,不用再次进入add递归
                        }


                    })
                }

            })
        }
    }

上面是从大家的回答中得到启发修改后的代码,只改动一小部分就可以了。

但是还是有一点不明白为什么按照题目原来的方法,即便任务三和任务四第一次进入add方法时都没resolve方法,但是可以抛弃他们第一次进入时的任务,在任务二执行完之后重新再初始化一次;但是为什么在任务二结束之后重新调用add(任务三或任务四)的时候,任务三和任务四却没办法执行then的内容。如果有大佬能具体知道什么原因可以告知一下,万分感谢

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题