【JavaScript】设计一个任务队列,控制请求并发数

1,260 阅读1分钟

要求如下:

  • 要求最大并发数 MAX
  • 每当有一个请求返回,就留下一个空位,可以增加新的请求
  • 所有请求完成后,结果按照 传入的任务 里面的顺序依次打出

思路: 任务队列 + max最大并发数 + 递归

我们将所有的请求放入到一个任务队列中。

每次从任务队列中拿取数量不超过 MAX 的任务并执行。

每拿出一个任务,MAX减1。

每执行完一个任务,MAX加1,将结果保留,并递归执行next函数,也就是执行任务队列中剩余的任务

同时,我们用一个count标识来统计已经执行完毕的任务数量,所有任务都执行完毕,则resolve结果队列

代码执行完毕

实现代码:

        class TaskQueue {
            constructor(max) {
                this.max = max // 最大并发数
                this.queue = [] // 任务队列
                this.resultArr = [] // 结果队列
                this.count = 0 // 任务数
            }

            /* 
            加入任务队列
            */
            addTask(t) {
                this.queue.push({
                    t, // 任务
                    index: this.count // 用来保存任务的顺序,保证结果是有序的
                })
                this.count++
            }

            /* 
            开始执行任务
            */
            run() {
                return new Promise(resolve => {
                    this.next(resolve)
                })
            }

            /*
            从任务队列中取出任务并执行
            */
            next(resolve) {
                let length = this.queue.length 
                if (length === 0) {
                    return
                }
                let min = Math.min(this.max, length) // 每次拿取的任务数不能超过max和队列长度,所以取最小值
                for (let i = 0; i < min; i++) {
                    let {t,index} = this.queue.shift()
                    this.max-- 
                    t().then(result => {
                            console.log(result)
                            this.resultArr[index] = result //任务执行完毕,有序保留结果
                        })
                        .catch(error => {
                            console.log(error)
                        })
                        .finally(() => {
                            this.count-- // 任务数-1
                            if (this.count === 0) { // 所有任务都已经完成
                                resolve(this.resultArr)  
                            } 
                            this.max++ // 留出空位给下一个任务
                            this.next(resolve) // 执行下一次循环
                        })
                }
            }
        }

测试

        const task = (i) => {
            return () => {
                return new Promise(resolve => {
                    setTimeout(() => {
                        resolve(i)
                    }, 2500);
                })
            }
        }

        async function test() {
            const taskQueue = new TaskQueue()
            for (let i = 0; i < 10; i++) {
                taskQueue.addTask(task(i))
            }
            let p = await taskQueue.run()
            console.log(p)
        }

        test()