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

相关推荐
这个昵称也不能用吗?6 分钟前
react-native搭建开发环境过程记录
前端·react native·cocoapods
hy_花花6 分钟前
Vue3.4之defineModel的用法
前端·vue.js
DataFunTalk20 分钟前
Foundation Agent:深度赋能AI4DATA
前端·后端·算法
hboot22 分钟前
rust 全栈应用框架dioxus
前端·rust·全栈
我是仙女你信不信27 分钟前
生成pdf并下载
前端·javascript·vue.js
少糖研究所27 分钟前
记一次Web Worker的使用
前端·性能优化
乔乔不姓乔呀30 分钟前
pc 和大屏如何适配
前端
speedoooo40 分钟前
新晋前端框架技术:小程序容器与SuperApp构建
前端·小程序·前端框架·web app
vvilkim1 小时前
React 组件类型详解:类组件 vs. 函数组件
前端·javascript·react.js
猫了个咪丶1 小时前
一文教你搞懂如何消除异步函数的传染性
前端