遍历对象调用API存库时出现JSON.parse解析错误(524超时)
嘿,我看你遇到的是短时间批量请求触发Cloudflare 524超时的问题——这时候API返回的是HTML页面而非预期的JSON,自然会让JSON.parse()抛出「Unexpected token <」的语法错误。下面给你几个针对性的解决方案,附代码修改示例:
核心问题分析
你用async.forEach无限制地并发请求,短时间内的高频访问触发了Cloudflare的超时保护(524错误),导致服务返回了错误页面的HTML,而非你需要的JSON数据,这才引发了解析失败。
1. 给请求加限流和延迟
先控制并发请求数,再给每个请求加随机延迟,模拟正常的访问节奏,避免被判定为恶意请求:
// 改用forEachLimit限制并发数,比如同时只发5个请求 async.forEachLimit(players, 5, function(item, asyncCallback) { var playerAccountId = (item.accountId).replace(/-/gi, ''); var kills = item.kills; var placement = item.placement; let options = { url: `https://fortnite-api.theapinetwork.com/prod09/users/public/br_stats_v2?user_id=${playerAccountId}`, method: 'GET', headers: { 'content-type': 'application/json', 'Authorization' : 'c825697ee0b871c684332601b46c2b71' } }; // 加1-3秒的随机延迟,避开高频请求 setTimeout(() => { function requestData(callback) { request(options, function(err, res, body) { // 先处理请求错误 if (err) { console.error(`请求玩家${playerAccountId}失败:`, err); return callback(err); } // 先验证响应是否为JSON if (!res.headers['content-type']?.includes('application/json')) { console.warn(`玩家${playerAccountId}返回非JSON内容,状态码: ${res.statusCode}`); console.log("响应内容片段:", body.slice(0, 200)); // 打印开头看是什么内容 return callback(new Error('非JSON响应')); } // 包裹JSON.parse在try-catch里,避免崩溃 try { body = body.trim(); let json = JSON.parse(body); let username = json.data.username; return callback(null, username); } catch (parseErr) { console.error(`解析玩家${playerAccountId}的响应失败:`, parseErr); console.log("响应内容:", body); return callback(parseErr); } }); } function doSomething(name) { var player = { "Name": name, "Account ID": playerAccountId, "Kills": kills, "Placement": placement } console.log(player); } requestData(function(err, name) { if (err) { // 出错后不要中断整个流程,记录日志后继续下一个 console.error(`处理玩家${playerAccountId}出错:`, err); } else { doSomething(name); } // 必须调用async的回调,告知当前迭代完成 asyncCallback(); }); }, Math.floor(Math.random() * 2000) + 1000); // 1-3秒随机延迟 });
2. 添加重试机制应对临时超时
遇到524这类临时错误时,自动重试请求,提高成功率:
// 给requestData加重试逻辑,最多重试3次 function requestData(retries = 3, callback) { request(options, function(err, res, body) { // 请求出错或者遇到524超时,且还有重试次数的话,延迟重试 if ((err || res.statusCode === 524) && retries > 0) { console.log(`玩家${playerAccountId}请求失败/超时,剩余重试次数: ${retries-1}`); setTimeout(() => requestData(retries-1, callback), 3000); return; } // 后续的响应验证和JSON解析逻辑和上面一致 if (!res.headers['content-type']?.includes('application/json')) { console.warn(`玩家${playerAccountId}返回非JSON内容,状态码: ${res.statusCode}`); return callback(new Error('非JSON响应')); } try { body = body.trim(); let json = JSON.parse(body); let username = json.data.username; return callback(null, username); } catch (parseErr) { console.error(`解析玩家${playerAccountId}的响应失败:`, parseErr); return callback(parseErr); } }); }
几个关键注意事项
- 别忘调用
async的回调:你原来的代码里没有触发async.forEach的callback,这会导致async无法跟踪迭代进度,甚至引发内存泄漏,上面的修改已经补上了。 - 提前验证响应:在解析JSON前先检查
Content-Type头和状态码,提前过滤非预期响应,避免不必要的解析错误。 - 保护API密钥:代码里直接硬编码了Authorization密钥,建议用环境变量(比如
process.env.API_KEY)来管理,避免泄露。
内容的提问来源于stack exchange,提问作者nhaniph
相关产品推荐
相关产品推荐

