- 作者:老汪软件技巧
- 发表时间:2024-09-11 11:01
- 浏览量:
前言
面试时,面试官问一次性发送100个请求,但是最多一次性接受50个请求,我们该如何处理?这个时候我们不能慌,因为控制并发会来拯救你.控制并发的主要作用就是确保事务的正确执行,即使是有多个事务同时执行,也会报错数据的一致性和完整性.下面我们就来实现控制并发吧.
正文
逻辑梳理
我们来看这张简陋的图片,假设一次性最多能处理两个事务,这里一共6个事务,事务1执行完所需时间为3秒,事务2执行完的时间为4秒,事务3执行完时间为1秒,事务4执行完所需时间为3秒,事务5执行完时间为4秒,事务6执行完时间为7秒.那么最开始执行的是事务1和事务2,由于事务1的执行时间比事务2的执行时间快1秒,事务1先执行完,此时此刻在执行队列中正在运行的事务量只有1,那么事务3会执行,事务3的执行时间为1秒钟,此时事务2执行时间的4秒也刚好结束.事务2,事务3都执行完毕,接下来就是事务4和事务5执行,所需时间分别是3秒和4秒,事务4会先执行完毕,然后事务6就会上来执行.
代码实现
第一步我们先写一个定时器,用于等待这一个过程
function timeout(time) {
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, time)
})
}
第二步,我们就需要如何控制并发量,也就是事务正在运行时的数量控制为2,这里我们使用Class面向编程对象.
class SuperTask {
constructor(paralleCount = 2) {
this.paralleCount = paralleCount
this.tasks = []
this.runningCount = 0
}
add(task) {
return new Promise((resolve, reject) => {
this.tasks.push({
task, resolve, reject
})
this._run()
})
}
_run() {
while(this.runningCount < this.paralleCount && this.tasks.length) {
const { task, resolve, reject } = this.tasks.shift()
this.runningCount++
task().then(resolve,reject).finally(() =>{
this.runningCount--
this._run()
})
}
}
}
我们在构造函数constructor初始化两个属性,this.paralleCount表示并发数,默认值为2,一次性能最多执行多少项事务.this.tasks 用于存储待执行的事务,this.runningCount表示当前正在运行的事务的数量.
两个静态方法add(),_run().在add()中,我们接受一个事务,并将其打包成一个对象,对象中有Promise中的resolve(),reject()函数.然后再将对象存入待执行的事务的数组tasks.最后一步调用_run().在_run()中会检查当前的运行的事务数量this.runningCount是否会小于并发数this.paralleCount且是否存在待执行的事务this.tasks.length.如果条件成立,就会取出第一个事务,并解构出事务task,resolve()和reject()两个函数.task().then(resolve,reject).finally()这里的finally()是指,不管这个Promise走的是resolve()还是reject(),finally()终究会执行,执行后就代表这个取出的事务被执行完毕了,那么正在运行的事务的数量的值就需要减一.然后再次调用_run().
const superTask = new SuperTask()
function addTask(time, name) {
superTask
.add(() => timeout(time))
.then(() => {
console.log(`任务${name}完成`);
})
}
addTask(10000, 1)
addTask(2000, 2)
addTask(5000, 3)
addTask(1000, 4)
addTask(7000, 5)
addTask(3000, 6)
最后实例化SuperTacks,创建一个用于添加事务的函数addTask()接受两个参数,一个是事务所执行的时间,一个是事务的名称.最后让我们看看输出结果