最近刷论坛突然看见了这几个javaScript流程并发的题,之前碰到过但是没做出来,当时做完也没有去找相关的题解,就一直搁置着。正好这次碰到了,于是决定把这几个题弄懂。
感觉这俩题比较考察对于js中的promise以及class,function的理解,虽然第一次看会觉得绕,but熟悉之后还是比较简单的。
如有错误,欢迎指正。
问题1
题目描述
实现一个 taskpool类,其至少具有 add 方法和最大并发数 max,该 add 方法接收函数(返回值为 promise),当当前执行的任务数小于设定值 max 时,立即执行,大于 max,则等待任务空闲时执行该任务,模板任务如下:
class TaskPool {
// 在此处写下你的实现
}
const taskpool = new TaskPool(2);
for (let i = 0; i < 10; i++) {
const task = () => new Promise(resolve => {
// 这里 i 的值也是以前非常高频的闭包题哦
setTimeout(() => {
console.log(`task${i} complete`);
resolve(`task${i}`);
}, 2000);
});
schedual.add(task);
}
// 预期输出
// 2s 后
// task0 complete
// task1 complete
// 再 2s 后
// task2 complete
// task3 complete
// 再 2s 后
// task4 complete
// task5 complete
// ...
// task8 complete
// task9 complete
思路
整体思路感觉比较简单(看过解析后觉得简单,but自己当时没想出来hhh):用一个变量temp去记录当前执行的任务数,涉及一个队列,每次添加任务/任务执行结束,都进行以下判断:
- 判断temp是否小于max
- 若小于:temp++,第一个任务出队并执行,之行结束后,temp--,回到第一步
- 若大于:不处理
代码
class TaskPool {
// 最大并发数
max=0;
// 当前并发数
cur=0
constructor(num){
this.max=num;
}
//往任务队列里面添加任务,并执行run 方法
add(task){
this.tasks.push(task);
this.run()
}
run(){
//若还有任务&&当前并发数小于最大并发数,就执行任务
while(this.tasks.length&&this.cur<this.max){
const tempTask=this.tasks.shift();
this.cur++;
//promise的finally函数,当状态变成rejected/fullfilled的时候执行
tempTask().finally(()=>{
this.cur--;
//这样保证了任务执行的顺序
this.run();
})
}
}
}
const taskpool = new TaskPool(2);
for (let i = 0; i < 10; i++) {
const task = () => new Promise(resolve => {
// 这里 i 的值也是以前非常高频的闭包题哦
setTimeout(() => {
console.log(`task${i} complete`);
resolve(`task${i}`);
}, 2000);
});
schedual.add(task);
}
// 预期输出
// 2s 后
// task0 complete
// task1 complete
// 再 2s 后
// task2 complete
// task3 complete
// 再 2s 后
// task4 complete
// task5 complete
// ...
// task8 complete
// task9 complete
问题2
题目描述
计一个函数 schedule,schedule 接收一个数量 n,返回一个新函数,新函数接受一个返回 promise 的函数数组,会按照顺序执行函数,并且同时执行的函数数量不超过 n。并且该函数的返回值是一个 promsie,该 promise 会在所有函数执行完后 resolve, 其值是函数数组的返回值按顺序构成的数组,模板任务如下:
function schedule(n) {
// 在此处写下你的实现
}
const runTask = schedule(4);
const tasks = new Array(10).fill(0).map((x, i) => () => new Promise(resolve => {
setTimeout(() => {
console.log(`task${i} complete`);
resolve(`task${i}`);
}, 2000);
}));
runTask(tasks).then(console.log);
// 预期输出
// 2s 后
// task0 complete
// task1 complete
// task2 complete
// task3 complete
// 再2s 后
// task4 complete
// task5 complete
// task6 complete
// task7 complete
// 再2s 后
// task8 complete
// task9 complete
// ['task0', 'task1', ..., 'task9']
思路
整体思路和上一题几乎一样,不同的是,上一题是class,这一题是function,所以一些细节需要去注意。
代码
如果要问为啥要套这么多层的return,请看模板代码中是如何调用目标函数。
function schedule(n) {
// 在此处写下你的实现
return function(tasks){
//记录总任务数
let len=tasks.length;
//保存执行结果
const res=[];
// 《待执行》的任务队列
let queue=[];
// 正在执行的任务数
let cur=0
// 执行完成的任务数
let finished=0
return new Promise((resolve)=>{
// 执行task,类似于上一题的run函数
function runTask(){
// 判断当前执行中的任务数量和待执行的数量
while(cur<n&&tasks[0]){
const item=tasks.shift();
// 执行任务
item().then((result)=>{
res.push(result)
cur--
finished++;
//执行完后,判断是继续执行递归,还是执行结束打印res
if(finished==len){
resolve(res)
}else{
runTask(resolve)
}
})
cur++
}
}
runTask()
})
}
}