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~~~

相关推荐
AI_56784 分钟前
Webpack性能优化终极指南:4步实现闪电打包
前端·webpack·性能优化
威风的虫26 分钟前
ES6 数组方法:告别循环,拥抱函数式编程
开发语言·前端·javascript
小杨快跑~28 分钟前
ES6 Promise:告别回调地狱的异步编程革命
前端·javascript·ecmascript·es6
linweidong35 分钟前
VIVO前端面试题及参考答案
前端·跨域·localstorage·重绘·浏览器兼容·git管理·前端重构
有意义36 分钟前
从零搭建:json-server+Bootstrap+OpenAI 全栈 AI 小项目
前端·后端·llm
温宇飞37 分钟前
CCState:为大型 Web 应用设计的状态管理库
前端
r0ad1 小时前
读诗的时候我却使用了自己研发的Chrome元素截图插件
前端·javascript·chrome
嵌入式学习之旅2 小时前
嵌入式面试1103
面试·职场和发展
蒙奇D索大2 小时前
【算法】递归算法实战:汉诺塔问题详解与代码实现
c语言·考研·算法·面试·改行学it
IT_陈寒2 小时前
React性能优化实战:这5个Hooks技巧让我的应用快了40%
前端·人工智能·后端