首先理清思路
请求并发控制做的是,同时仅能有 <=某个个数的请求在网络中传输。
//模拟网络请求
function delay(delay) {
return new Promise(res => {
setTimeout(res, delay)
})
}
//参数为最大请求并发数
const superTask = new SuperTask(2);
superTask.addTask(() => delay(2000), 4).then(() => console.log('Task 1 completed'));
superTask.addTask(() => delay(500), 5).then(() => console.log('Task 2 completed'));
superTask.addTask(() => delay(5000), 1).then(() => console.log('Task 3 completed'));
这段代码的输出结果:
Task 1 completed
Task 2 completed
Task 3 completed
首先先对任务进行排序,高优先级的先进性(权重越小,优先级越高)
//排序结果
Task3
Task1
Task2
所以先运行task3和task1,现在正在运行的任务为2和容量相同了,不能在运行任务了,需要等待正在执行的任务,执行完成才可以开启task2任务,等待2s后task1执行完成,开始执行执行task2,等待0.5s后task2执行完成,在等待2.5后task3执行完成。
要实现这个功能需要什么条件
- 需要一个可以自动对任务按照权重进行排序的数据结构
使用heap堆(小根堆),当然也可以每次都对数组排序但是时间复杂较高(nlogn)而堆的存取时间复杂为logn
- 什么时候要执行任务
用户调用addTask方法添加任务的时候去试探执行
当任务完成得时候再去试探是否可以执行
- 什么条件可以执行任务
堆还有任务
正在执行的任务数量要小于容量
代码
先定义属性
class SuperTask {
constructor(cap) {
//容量
this.cap = cap
//小根堆,用来存储任务
this.heap = new Heap((a, b) => a.priority - b.priority)
//正在执行的任务数量
this.runningCount = 0
}
//添加任务
addTask(task, priority = 0) {
return new Promise((res, rej) => {
})
}
//执行任务
__runtask() {
}
}
我们看测试用法
superTask.addTask(() => delay(500), 5).then(() => console.log('Task 2 completed'));
addTask方法需要返回一个promise,并且promise的状态和task是同步的,这个要如何实现? 我们只需要在加添任务的时候,将promise的reslove,reject函数也添加进行即可,这样每个任务就和addTask方法的promise挂钩了。
addTask(task, priority = 0) {
const p = new Promise((res, rej) => {
this.heap.push({
task,
res,
rej,
priority: priority
})
})
this.__runtask()
return p
}
再来实现一下__runtask函数,思路比较简单,只要可以执行,就一直递归调用__runtask方法执行。
__runtask() {
while (this.runningCount < this.cap && this.heap.size()) {
const {task, res, rej} = this.heap.pop();
this.runningCount++;
task().then(res).catch(rej).finally(() => {
this.runningCount--;
this.__runtask();
})
}
}
你觉这样就可以了吗,我们来试一下
import Heap from 'heap'
function delay(delay) {
return new Promise(res => {
setTimeout(res, delay)
})
}
class SuperTask {
constructor(cap) {
this.cap = cap
this.heap = new Heap((a, b) => a.priority - b.priority)
this.runningCount = 0
}
addTask(task, priority = 0) {
const p = new Promise((res, rej) => {
this.heap.push({
task,
res,
rej,
priority: priority
})
})
this.__runtask()
return p
}
__runtask() {
while (this.runningCount < this.cap && this.heap.size()) {
const {task, res, rej} = this.heap.pop();
this.runningCount++;
task().then(res).catch(rej).finally(() => {
this.runningCount--;
this.__runtask();
})
}
}
}
const superTask = new SuperTask(2);
superTask.addTask(() => delay(2000), 4).then(() => console.log('Task 1 completed'));
superTask.addTask(() => delay(500), 5).then(() => console.log('Task 2 completed'));
superTask.addTask(() => delay(5000), 1).then(() => console.log('Task 3 completed'));
输出结果
这不对啊
正确结果不应该是
Task 1 completed
Task 2 completed
Task 3 completed
哪里错了呢,是因为我们执行addTask方法的时候直接就__runtask运行任务,任务还没来得及进行排序,那如何做到这个呢? 我们想要任务排序,就不能立即执行任务,需要等待任务全部进入到heap在去执行任务,我们可以利用事件循环机制,我们知道微任务 / 宏任务,都会等待主线程执行完成,主线程执行完成,heap中不就存储了全部任务了嘛。 修改代码:
import Heap from 'heap'
function delay(delay) {
return new Promise(res => {
setTimeout(res, delay)
})
}
class SuperTask {
constructor(cap) {
this.cap = cap
this.heap = new Heap((a, b) => a.priority - b.priority)
this.runningCount = 0
}
addTask(task, priority = 0) {
const p = new Promise((res, rej) => {
this.heap.push({
task,
res,
rej,
priority: priority
})
})
setTimeout(() => {this.__runtask()}, 0)
return p
}
__runtask() {
while (this.runningCount < this.cap && this.heap.size()) {
const {task, res, rej} = this.heap.pop();
this.runningCount++;
task().then(res).catch(rej).finally(() => {
this.runningCount--;
this.__runtask();
})
}
}
}
const superTask = new SuperTask(2);
superTask.addTask(() => delay(2000), 4).then(() => console.log('Task 1 completed'));
superTask.addTask(() => delay(500), 5).then(() => console.log('Task 2 completed'));
superTask.addTask(() => delay(5000), 1).then(() => console.log('Task 3 completed'));
看输出:
对了!!!
但是这样真的可以嘛,比如说我们有1000000个任务,那岂不是宏任务中要存储1000000个任务,但是真的有必要嘛,其实只需要有一次setTimeout(() => {this.__runtask()}, 0)就够了。
这个就很简单了,我们只需要搞一个变量,标志目前是否有setTimeOut回调即可。
补全代码;
let flag = false
addTask(task, priority = 0) {
const p = new Promise((res, rej) => {
this.heap.push({
task,
res,
rej,
priority: priority
})
})
if(!flag) {
flag = true
setTimeout(() => {
this.__runtask()
flag = false;
}, 0)
}
return p
}
加个console.log验证一下:
import Heap from 'heap'
function delay(delay) {
return new Promise(res => {
setTimeout(res, delay)
})
}
let flag = false
class SuperTask {
constructor(cap) {
this.cap = cap
this.heap = new Heap((a, b) => a.priority - b.priority)
this.runningCount = 0
}
addTask(task, priority = 0) {
const p = new Promise((res, rej) => {
this.heap.push({
task,
res,
rej,
priority: priority
})
})
if(!flag) {
flag = true
setTimeout(() => {
//新增log
console.log('执行了')
this.__runtask()
flag = false;
}, 0)
}
return p
}
__runtask() {
while (this.runningCount < this.cap && this.heap.size()) {
const {task, res, rej} = this.heap.pop();
this.runningCount++;
task().then(res).catch(rej).finally(() => {
this.runningCount--;
this.__runtask();
})
}
}
}
const superTask = new SuperTask(2);
superTask.addTask(() => delay(2000), 4).then(() => console.log('Task 1 completed'));
superTask.addTask(() => delay(500), 5).then(() => console.log('Task 2 completed'));
superTask.addTask(() => delay(5000), 1).then(() => console.log('Task 3 completed'));
输出:
正确,log仅输入了一次
全部代码
import Heap from 'heap'
function delay(delay) {
return new Promise(res => {
setTimeout(res, delay)
})
}
let flag = false
class SuperTask {
constructor(cap) {
this.cap = cap
this.heap = new Heap((a, b) => a.priority - b.priority)
this.runningCount = 0
}
addTask(task, priority = 0) {
const p = new Promise((res, rej) => {
this.heap.push({
task,
res,
rej,
priority: priority
})
})
if(!flag) {
flag = true
setTimeout(() => {
this.__runtask()
flag = false;
}, 0)
}
return p
}
__runtask() {
while (this.runningCount < this.cap && this.heap.size()) {
const {task, res, rej} = this.heap.pop();
this.runningCount++;
task().then(res).catch(rej).finally(() => {
this.runningCount--;
this.__runtask();
})
}
}
}
const superTask = new SuperTask(2);
superTask.addTask(() => delay(2000), 4).then(() => console.log('Task 1 completed'));
superTask.addTask(() => delay(500), 5).then(() => console.log('Task 2 completed'));
superTask.addTask(() => delay(5000), 1).then(() => console.log('Task 3 completed'));