js异步请求池,控制并发请求数量

Q:并发请求数量过大,会在短时间内发送大量的网络请求,并且占用大量的系统资源,可能会造成接口阻塞,浏览器卡死现象,怎么才能控制并发请求数量防止频繁渲染呢?

可以考虑使用异步请求池,来控制同一时间并发请求的数量,我们可以通过维护一个请求队列来实现:

1.创建类并维护变量

定义一个PromisePool的类,接收conncurrency参数来控制并发请求的数量;

同时我们需要存储正在执行的请求数running,请求队列queue,所有的请求结果result。

js 复制代码
class PromisePool {
    constructor(concurrency) {
        this.concurrency = concurrency;
        this.running = 0;
        this.queue = [];
        this.results = [];
    }
}

2.定义类方法

  • 首先我们需要将请求传入实例对象中,则需要定义add方法,返回一个promise对象,通过.then()方法接收接口返回值;
  • 其次,将请求传入后需要控制请求的执行,则需要定义run方法,也返回一个promise对象,核心逻辑是根据running和concurrensy判断当前请求需要执行或进入queue队列;
  • 如果当前running < concurrency,则需要从队列中取请求执行,这里需要定义next()方法;
js 复制代码
class PromisePool {
    constructor(concurrency) {
        this.concurrency = concurrency;
        this.running = 0;
        this.queue = [];
        this.results = [];
        this.taskCallbacks = [];
    }

    add(...tasks) {
        const taskPromises = tasks.map(task => this.run(task))
        return Promise.all(taskPromises);
    }

    run(task) {
        return new Promise((resolve, reject) => {
            const warppedTask = async () => {
                try {
                    const result = await task();
                    this.results.push(result);
                    resolve(result);
                } catch (error) {
                    reject(error);
                } finally {
                    this.running--;
                    this.next();
                }
            }
            if (this.running < this.concurrency) {
                this.running++;
                warppedTask();
            } else {
                this.queue.push(warppedTask);
            }
        })
    }

    next() {
        if (this.queue.length > 0 && this.running < this.concurrency) {
            const task = this.queue.shift();
            this.running++;
            task();
        }
    }
}

3.使用

js 复制代码
const task = [
    () => new Promise(resolve => {
        setTimeout(() => {
            resolve('task1');
        }, 1000)
    }),
    () => new Promise(resolve => {
        setTimeout(() => {
            resolve('task2');
        }, 2000)
    }),
    () => new Promise(resolve => {
        setTimeout(() => {
            resolve('task3');
        }, 3000)
    }),
    () => new Promise(resolve => {
        setTimeout(() => {
            resolve('task4');
        }, 4000)
    }),
    () => new Promise(resolve => {
        setTimeout(() => {
            resolve('task5');
        }, 1000)
    }),
    () => new Promise(resolve => {
        setTimeout(() => {
            resolve('task6');
        }, 4000)

    }),
    () => new Promise(resolve => {
        setTimeout(() => {
            resolve('task7');
        }, 1000)
    }),
]
pool.add(task[0]).then(result => {
    console.log('任务完成:', result);
});
pool.add(task[1]).then(result => {
    console.log('任务完成:', result);
});
pool.add(task[2]).then(result => {
    console.log('任务完成:', result);
});
pool.add(task[3]).then(result => {
    console.log('任务完成:', result);
});
pool.add(task[4]).then(result => {
    console.log('任务完成:', result);
});
pool.add(task[5]).then(result => {
    console.log('任务完成:', result);
});

pool.add(() => new Promise(resolve => {
    setTimeout(() => {
        resolve('单独任务');
    }, 1000)
})).then(result => {
    console.log('任务4完成:', result);
});

输出:

4.传入数组,监听返回

这里通过注册任务完成的回调函数实现,在每个接口resolve时调用函数

js 复制代码
class PromisePool {
    constructor(concurrency) {
        this.concurrency = concurrency;
        this.running = 0;
        this.queue = [];
        this.results = [];
        this.taskCallbacks = [];
    }

    onTaskDone(callback) {
        this.taskCallbacks.push(callback);
        return this;
    }

    add(...tasks) {
        const taskPromises = tasks.map(task => this.run(task))
        return Promise.all(taskPromises);
    }

    run(task) {
        return new Promise((resolve, reject) => {
            const warppedTask = async () => {
                try {
                    const result = await task();
                    this.results.push(result);
                    this.taskCallbacks.forEach(cb => cb(result));
                    resolve(result);
                } catch (error) {
                    reject(error);
                } finally {
                    this.running--;
                    this.next();
                }
            }
            if (this.running < this.concurrency) {
                this.running++;
                warppedTask();
            } else {
                this.queue.push(warppedTask);
            }
        })
    }

    next() {
        if (this.queue.length > 0 && this.running < this.concurrency) {
            const task = this.queue.shift();
            this.running++;
            task();
        }
    }
}

let pool = new PromisePool(3);

const task = [
    () => new Promise(resolve => {
        setTimeout(() => {
            resolve('task1');
        }, 1000)
    }),
    () => new Promise(resolve => {
        setTimeout(() => {
            resolve('task2');
        }, 2000)
    }),
    () => new Promise(resolve => {
        setTimeout(() => {
            resolve('task3');
        }, 3000)
    }),
    () => new Promise(resolve => {
        setTimeout(() => {
            resolve('task4');
        }, 4000)
    }),
    () => new Promise(resolve => {
        setTimeout(() => {
            resolve('task5');
        }, 1000)
    }),
    () => new Promise(resolve => {
        setTimeout(() => {
            resolve('task6');
        }, 4000)

    }),
    () => new Promise(resolve => {
        setTimeout(() => {
            resolve('task7');
        }, 1000)
    }),
]

调用函数

js 复制代码
// 注册任务完成回调(实时获取结果)
pool.onTaskDone(result => {
    console.log('实时结果:', result);
});

pool.add(...task).then(result => {
    console.log('任务完成:', result);
});

结果

end~~~

相关推荐
jerrywus2 分钟前
为什么每个程序员都应该试试 cmux:AI 加持的终端效率革命
前端·人工智能·claude
codeniu8 分钟前
@logicflow/vue-node-registry 在 Vite 中无法解析的踩坑记录与解决方案
前端·javascript
孟祥_成都15 分钟前
AI 术语满天飞?90% 的人只懂名词,不懂为什么!
前端·人工智能
Lupino41 分钟前
被 React “玩弄”的 24 小时:为了修一个不存在的 Bug,我给大模型送了顿火锅钱
前端·react.js
米丘1 小时前
了解 Javascript 模块化,更好地掌握 Vite 、Webpack、Rollup 等打包工具
前端
Heo1 小时前
深入 React19 Diff 算法
前端·javascript·面试
滕青山1 小时前
个人所得税计算器 在线工具核心JS实现
前端·javascript·vue.js
小怪点点1 小时前
手写promise
前端·promise
国思RDIF框架1 小时前
RDIFramework.NET Web 敏捷开发框架 V6.3 发布 (.NET8+、Framework 双引擎)
前端
Mintopia1 小时前
如何在有限的时间里,活出几倍的人生
前端