• 作者:老汪软件技巧
  • 发表时间: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()接受两个参数,一个是事务所执行的时间,一个是事务的名称.最后让我们看看输出结果