JS中while循环内async axios请求未等待执行完成问题排查
问题根因
- 核心错误:
while循环是同步执行逻辑,你在循环内部包裹axios请求的立即执行异步函数没有加await阻塞,循环触发异步任务后会直接进入下一轮迭代,完全不会等待请求返回、task状态更新,最终导致循环无等待快速空转、请求乱发。 - 附带的其他逻辑bug:
- 代理重复获取:switch逻辑外先调用了一次
getNextProxy(),异步函数内部又调用了一次,两次拿到的代理不一致,会导致代理调度混乱。 - axios传参错误:你把
timeout:5000单独作为第三个参数传入,实际上axios所有配置都要放在第二个参数对象里,当前写法超时配置完全不生效。 - 循环终止逻辑失效:
case "new"中判断url == "end"时的break只能跳出switch分支,无法终止外层while循环,会继续往下执行无效请求。 - 无重试上限:遇到验证码、请求错误就切代理重试的逻辑没有次数限制,代理池耗尽时会触发死循环。
- 翻页逻辑缺失:
case "next"分支没有对pageNumber做自增,会重复请求同一页内容。
- 代理重复获取:switch逻辑外先调用了一次
修正后代码
把整个爬取逻辑包裹在异步函数中,每轮循环等待当前请求执行完成后再进入下一轮,同时修复上述逻辑问题:
const MAX_RETRY = 10; // 最大代理重试次数,避免死循环 let retryCount = 0; async function startCrawl() { let task = "new"; let pageNumber = 1; let url = ""; while( (task == "new") || (task == "next") || (task == "proxy")) { // 重试次数超限直接终止 if (retryCount >= MAX_RETRY) { console.log("Retry limit reached, crawl stopped"); return; } switch(task) { case "new": pageNumber = 1; url = getNextUrl(pageNumber); if(url == "end") { console.log("Search completed!"); return; // 直接终止函数,跳出循环 } retryCount = 0; // 新任务重置重试计数 break; case "next": pageNumber += 1; url = getNextUrl(pageNumber); retryCount = 0; // 正常翻页重置重试计数 break; case "proxy": retryCount += 1; break; } // 单次只取一次代理,避免重复调度 const proxy = getNextProxy().toString().split(":"); console.log("Using proxy:", proxy); console.log("Request url:", url); try { const response = await axios.get(url,{ headers: { 'user-agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.66 Safari/537.36" }, proxy: false, timeout: 5000, // timeout放到配置对象内部 httpsAgent: new HttpsProxyAgent.HttpsProxyAgent(`http://${proxy[0]}:${proxy[1]}`) }); const $ = cheerio.load(response.data); const captcha = $("#captcha-form"); if(captcha.length > 0) { task = "proxy"; continue; // 直接进入下一轮循环切代理 } const searchitems = $(".jtfYYd"); for(let i=0; i < searchitems.length; i++) { const element = searchitems[i]; const link = $(element).find(".yuRUbf").find("a").attr('href'); const title = $(element).find("h3").text(); const details = $(element).find(".MUxGbd.wuQ4Ob.WZ8Tjf").find("span"); const stub = $(element).find(".VwiC3b.yXK7lf.MUxGbd.yDYNvb.lyLwlc.lEBKkf").find("span").text(); console.log(); console.log("Title: " + title); console.log("Link: " + link); console.log("Details: " + details); console.log("Stub: " + stub); } const botstuff = $("#botstuff"); console.log("botstuff: " + botstuff.text()); if(botstuff.text().indexOf("Next") > 1) { task = "next"; } else { task = searchitems.length == 0 ? "proxy" : "new"; } } catch (error) { // 请求出错切代理重试 task = "proxy"; } } } // 启动爬虫 startCrawl();
补充说明
- 修正后统一用
async/await语法代替了原来混用的.then()写法,逻辑更清晰,每轮循环会阻塞等待当前axios请求返回、task状态更新完成后才会进入下一轮,不会出现空转问题。 - 加了最大重试次数限制,避免代理失效时无限死循环占满资源。
- 去掉了重复的代理获取、重复变量定义逻辑,每轮请求只调度一个代理,不会出现代理不匹配的问题。
- 修正了axios配置参数的位置,超时规则可以正常生效。
内容的提问来源于stack exchange,提问作者user1672528
相关产品推荐
相关产品推荐

