写爬虫的时候,可能需要控制网络请求的并发数以避免被反爬。我们知道 Promise 的结果是异步获取的,因此控制 Promise 的并发数的基本思路是
- 限制最大的并发数
- 在最大并发数内启动尽可能多的异步任务
- 通过 Promise 队列保存所有异步任务
先从最基本的情况开始——限制并发数为 1,也就是只有前一个 Promise 执行完成后下一个 Promise 才能执行。
// task 中的每一项都是一个返回 Promise 的函数
// 执行 task 的函数其实就是启动异步任务
// e.g.:
// function req = () => {
// return fetch(url, ...)
// }
const tasks = []
const limit = () => {
// 任务队列空,退出
if(!tasks.length) {
return ;
}
// 从队头取数据
const task = tasks.shift()
// task 执行完成后,递归
task().then(() => {
limit()
})
}
通过 .then 递归的执行异步任务,我们实现了限制并发数为 1 时的并发控制。
对于并发数 > 1 的情况,特殊的点在于初始化时需要先启动数量 <= 并发数的任务,之后继续进行递归。
const tasks = []
let max_limit = 3
const limit = () => {
// 任务队列空,退出
if(!tasks.length || max_limit <= 0) {
return ;
}
while(max_limit) {
// 最大并发数 - 1
max_limit--;
// 从队头取数据
const task = tasks.shift()
// task 执行完成后,递归
task().then(() => {
// 最大递归数 + 1
max_limit++
limit()
})
}
}
上述代码通过定义了一个全局变量 max_limit 控制并发数,实际应用中可以用闭包或者类等方法将 max_limit 控制在参数中,以减少耦合。