实现 Scheduler 中的 add 方法,满足:
- add 方法只有一个入参 task , 类型为 () => Promise<any>
- 按 add 调用顺序执行任务
- 同一时刻只能存在至多2个并发任务
- 可自由添加实例属性、方法
题目代码:
class Scheduler {
add(promiseMaker) {
}
}
const scheduler = new Scheduler();
const addTask = (time, text) => {
const promiseMaker = () => new Promise(resolve => {
setTimeout(() => {
console.log(text);
resolve();
}, time);
});
Scheduler.add(promiseMaker);
};
addTask(1000, '1');
addTask(500, '2');
addTask(300, '3');
addTask(400, '4');
// 最终打印 2 3 1 4
解决思路:
- 题目要求“按 add 调用顺序执行任务”,说明添加的任务会先保存进队列里(先进先出),而不是堆栈(后进先出),因此保存任务使用的数据结构为数组
- “同一时刻只能存在至多2个并发任务”,这里的关键题干是“同一时刻”,“2个并发任务”,再结合(1),可以想到Scheduler里会存在2个存放任务的数组,一个保存正在执行任务的数组doingFuncs ,当任务执行完成,从数组doingFuncs中移除任务(不用按照在数组里存在的顺序移除,谁先完成谁先移除),一个保存待执行任务的数组funcs,以及一个最大执行任务数maxFunNum。
- 两个数组之间的关系:当doingFuncs其中一个任务执行完成时,会从funcs头部取出一个任务塞入doingFuncs,那怎么表达一个任务执行完成呢?题目中任务的数据类型是promise,promise不管结果是成功还是失败,都会在then中处理执行完成之后的事务,因此事务主要处理2件事:
(1)从doingFuncs中移除任务
(2)从funcs中拿出一个任务,放入doingFuncs中执行
解决方案:
class Scheduler {
constructor() {
this.funcs = []; //待执行的任务
this.doingFuncs = []; //正在执行的任务数
this.maxFunNum = 2; //最大执行任务数
}
add(promiseMaker) {
if (this.doingFuncs.length < this.maxFunNum) {
this.run(promiseMaker);
} else {
this.funcs.push(promiseMaker);
}
}
run(promiseMaker) {
let arrayLength = this.doingFuncs.push(promiseMaker);
let index = arrayLength - 1;
promiseMaker().then(() => {
this.doingFuncs.splice(index, 1);
if (this.funcs.length > 0) {
this.run(this.funcs.shift());
}
})
}
}
const scheduler = new Scheduler();
const addTask = (time, text) => {
const promiseMaker = () => new Promise(resolve => {
setTimeout(() => {
console.log(text);
resolve();
}, time);
});
scheduler.add(promiseMaker);
};
addTask(1000, '1');
addTask(500, '2');
addTask(300, '3');
addTask(400, '4');
// 最终打印 2 3 1 4
结语:
这个代码编程题是我在面试字节跳动的时候遇到的,然后感觉很有趣但当时并没有实现,就把它记录下来,自己花时间实现了。其实了解这机制并不困难,重要的是如何举一反三,把机制用于实践中,因此根据这个机制我做了一个点餐排队系统。
更新在后续文章中。【新】点餐排队系统Demo