Skip to content

7. 控制并发请求

来源:百度二面

题目

实现一个并发控制器,限制同一时间最多只能有 n 个异步任务同时执行。

代码

js
// 控制并发请求
// 百度二面

// 模拟发送请求接收到数据:返回一个promise,time时间到了,promise完成
function timeout(time) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve()
        }, time)
    })
}

class SuperTask {
    constructor(maxCount = 2) {
        // 最大并发数量
        this.maxCount = maxCount
        // 请求队列
        this.requestQueue = []
        // 当前正在进行请求的并发数
        this.currentCount = 0
    }
    // 添加任务到请求队列中
    add(task) {
        return new Promise((resolve, reject) => {
            this.requestQueue.push({
                task,
                resolve,
                reject
            })
            // 尝试执行任务
            this.run()
        })
    }
    // 执行任务:判断当前的并发数,执行我们的任务
    run() {
        while (this.currentCount < this.maxCount && this.requestQueue.length !== 0) {
            // 拿出任务
            const { task, resolve, reject } = this.requestQueue.shift()
            this.currentCount++
            // 执行任务,在执行完毕后,需要再次调用run
            task().then(resolve, reject).finally(() => {
                // 任务执行完毕
                this.currentCount--
                this.run()
            })
        }
    }
}

const superTask = new SuperTask(2)

function addTask(time, name) {
    superTask.add(() => timeout(time)).then(() => {
            console.log(`任务${name}完成`)
        })
}

思路

使用队列 + 计数器实现一个简单的任务调度器。

  1. requestQueue 保存还没执行的任务
  2. currentCount 记录当前正在执行的任务数量
  3. 每次 add 新任务后,都尝试调用 run
  4. run 在并发数未满时,从队列中取出任务执行
  5. 任务结束后把 currentCount 减一,再继续调度后面的任务

关键点

  • maxCount 控制最大并发数
  • requestQueue 负责保证任务先进先出
  • finally 很关键,无论任务成功还是失败,都要释放并发占位
  • 每个任务执行完成后再次调用 run,可以持续拉起后续排队任务
  • 时间复杂度取决于任务数量,调度部分整体是 O(n)