循环调用返回JSON的API接口时程序中途报错,请求排查解决
嘿,我看你遇到的这个SyntaxError: Unexpected token...问题,本质是某个请求返回的内容不是合法JSON,导致JSON.parse(body)直接报错。结合你一次性发起1000个请求的场景,大概率是这几个原因,咱们一步步来解决:
1. 并发请求太猛被服务器限流了
你用同步循环发起异步请求,相当于瞬间给服务器打了1000个请求,几乎所有后端都会有并发限制,超过阈值后会返回429(请求过多)、503这类错误响应——这些响应通常是HTML错误页或者纯文本,根本不是JSON,解析肯定失败。
怎么改?控制并发数
别一次性发所有请求,改成串行或者限制同时发起的请求数。比如用p-limit(Node.js环境)来控并发,代码示例如下:
// 先装依赖:npm i p-limit const pLimit = require('p-limit'); const limit = pLimit(10); // 限制同时跑10个请求,数字可以根据服务器承受力调整 const fetchIssueCounts = async () => { // 把每个请求包装成受限制的异步函数 const requestPromises = Keys.map(key => limit(async () => { try { // 把回调式的httpRequest封装成Promise版本 const res = await new Promise((resolve, reject) => { httpRequest(`https://hostname/api/issues/projectKeys=${key}?format=jSON`) .auth(global.username, global.password) .end((err, res) => err ? reject(err) : resolve(res)); }); // 先校验状态码 if (res.statusCode !== 200) throw new Error(`状态码错误: ${res.statusCode}`); const jsonObj = JSON.parse(res.body); return jsonObj.total; } catch (err) { console.error(`处理密钥 ${key} 时出错:`, err.message); return null; // 出错就返回null,后续过滤掉 } })); // 等待所有请求完成 const issueCounts = await Promise.all(requestPromises); // 过滤无效数据 const validCounts = issueCounts.filter(count => count !== null); console.log(`成功获取 ${validCounts.length} 条有效数据`); }; fetchIssueCounts();
要是不想用第三方库,写个简单的串行循环也能搞定:
let currentIndex = 0; const issue = []; function fetchNextKey() { if (currentIndex >= Keys.length) { console.log('所有请求完成!共获取', issue.length, '条数据'); return; } const key = Keys[currentIndex]; currentIndex++; httpRequest(`https://hostname/api/issues/projectKeys=${key}?format=jSON`, function (err, res, body) { // 先处理请求错误 if (err) { console.error(`请求密钥 ${key} 失败:`, err); fetchNextKey(); return; } // 校验响应状态码 if (res.statusCode < 200 || res.statusCode >= 300) { console.error(`密钥 ${key} 返回错误状态码: ${res.statusCode},响应内容:`, body); fetchNextKey(); return; } // 包裹try-catch处理解析错误 try { const jsonObj = JSON.parse(body); issue.push(jsonObj.total); console.log('当前已获取', issue.length, '条数据'); } catch (parseErr) { console.error(`解析密钥 ${key} 的响应失败:`, parseErr.message); console.log('原始响应内容:', body); // 打印出来方便排查问题 } // 发起下一个请求 fetchNextKey(); }).auth(global.username, global.password); } // 启动串行请求 fetchNextKey();
2. 单个密钥无效/权限不足,返回非JSON内容
有些密钥可能本身就无效,或者你没有访问对应项目的权限,API返回的是错误提示(比如HTML页面、纯文本),不是合法JSON,自然解析失败。
怎么改?加完整的错误处理和校验
刚才的代码已经包含了这些关键处理:
- 先判断
err是否存在,处理请求层面的错误 - 检查响应状态码,只在成功状态下解析JSON
- 给
JSON.parse套try-catch,捕获解析错误,同时打印对应密钥和响应内容,方便定位哪个密钥出问题
另外还要注意var i的闭包陷阱!你原来的代码用var i,循环里的所有回调共享同一个i变量,当回调执行时i已经变成1000了,导致你根本不知道是哪个密钥出问题。改成let i或者把key存到局部变量里(比如上面代码里的const key = Keys[currentIndex])就能解决。
3. URL参数可能写错了?
你URL里的format=jSON,有些API对参数大小写敏感,是不是应该写成format=json?可以看看API文档确认下,要是参数写错了,API可能返回非JSON内容。
总结下核心要点:
- 控制并发数,别把服务器怼懵了
- 完善错误处理,每个环节都做校验
- 修复闭包陷阱,确保每个请求对应正确的密钥
内容的提问来源于stack exchange,提问作者Mudabbir Pasha

