题目
实现一个带并发限制的异步任务调度器,保证同时最多运行 2 个任务
题目代码:
class Scheduler {
async add(promiseCreator) {
// 补全代码
}
// 补全代码
}
const timeout = (time) =>
new Promise((resolve) => {
setTimeout(resolve, time);
});
const scheduler = new Scheduler({ limit: 2 });
const addTask = (time, order) => {
scheduler
.add(() => timeout(time))
.then(() => console.log(`order: ${order}`));
};
addTask(400, 4);
addTask(200, 2);
addTask(300, 3);
addTask(100, 1);
// 应该输出:2 4 3 1
思路
题目中的 add
方法前面有个 async
面试官的意思应该是想让面试者通过async/await
实现,但我当时并没有任何思路。我的实现思路是:
- 新建
Promise
并将resolve
和reject
保存起来 - 执行中任务未超过限制时,直接
run
执行任务 - 超过限制,缓存起来
run
执行任务完成后删除执行队列,并检查缓存队列执行任务
代码实现
class Scheduler {
constructor(opt = {}) {
const defaultOption = { limit: 2 };
const finalOption = Object.assign({}, defaultOption, opt);
this.queue = [];
this.holdOn = [];
this.limit = finalOption.limit;
this.id = 0;
}
add(promiseCreator) {
return new Promise((resolve, reject) => {
promiseCreator.resolve = resolve;
promiseCreator.reject = reject;
const len = this.queue.length;
if (len < this.limit) {
this.run(promiseCreator);
} else {
this.holdOn.push(promiseCreator);
}
});
}
run(promiseCreator) {
const id = this.id++;
let promise;
try {
promise = promiseCreator().then(() => {
// 删除已完成
this.queue = this.queue.filter((i) => i.id !== id);
// 执行等待队列
this.checkHoldOn();
promiseCreator.resolve();
});
} catch (error) {
promiseCreator.reject(error);
}
promise.id = id;
this.queue.push(promise);
}
checkHoldOn() {
if (this.holdOn.length > 0) {
const task = this.holdOn.shift();
this.run(task);
}
}
}
并没有用到 async/await
网上搜了一下,看到别人的实现,很惊艳,对我的启发很大,原来 async/await
还可以这样用:
class Scheduler2 {
list = []; //用来承载还未执行的异步
count = 0; //用来计数
constructor(limit) {
this.limit = limit; //允许同时运行的异步函数的最大个数
}
async add(fn) {
if (this.count >= this.limit) {
// 超出限制的任务会一直 await,直到进行中的任务resolve
await new Promise((resolve) => {
this.list.push(resolve);
});
}
this.count++;
const result = await fn();
this.count--;
// 进行中的任务执行完成,resolve() 的是在此任务之后添加的待执行任务,先进先出
if (this.list.length > 0) this.list.shift()();
return result;
}
}
分析: 1,2,3 会同时输出,过 2s 后又会同时输出 4,5,6.最需要引起注意的便是类 Scheduler
中的 add
方法中的第一句,通过 await
阻塞 Promise
但是又不执行 resolve
,而是将 resolve
保存到数组当中去,这样就达到了当异步任务超过 limit
时线程就会阻塞在第一行.每执行完一个异步任务就会去数组中查看一下有没有还处于阻塞当中的异步任务,如果有的话就执行最前面的那个异步任务.
参考:js 实现异步任务调度器
本文由 咻咻 创作,采用 知识共享署名4.0
国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: Jun 2,2020