Node.js中for循环调用API时id_Arr[i]无效问题排查
问题描述
尝试用for循环遍历字符串数组,将数组元素id_Arr[i]作为API链接的一部分发起请求。该逻辑在常规JS/Angular中正常运行,但在Node.js环境下,请求虽能发送,但id_Arr[i]未被识别为数组元素引用,拼出的URL导致返回结果提示端点不存在;若移除for循环,直接在API路径中写入具体ID则可正常运行。
具体流程:先调用API获取所有赛事信息,筛选出正在进行的赛事,将其ID存入id_Arr数组;随后通过for循环遍历id_Arr,使用数组中的ID拼接API链接发起GET请求。
代码如下:
const https = require('node:https'); arr = new Array(); id_Arr = new Array(); race_arr = new Array(); // Get Schedule var start = new Date(); https.get('link to api', (res) => { console.log('statusCode:', res.statusCode); console.log('headers:', res.headers); res.setEncoding('utf8'); let rawData = ''; res.on('data', (chunk) => { rawData += chunk; }); res.on('end', () => { try { const jsoData = JSON.parse(rawData); arr.push(jsoData.sport_events); // console.log(arr); var xm = arr[0].filter(function (item) { return item.status == 'live'; }); for (i = 0; i <= xm.length - 1; i++) { id_Arr.push(xm[i].id); } } catch (e) { console.error(e.message); } }); }).on('error', (e) => { console.error(e); }); // Get Live Circuit for (i = 0; i <= id_Arr.length - 1; i++) { https.get('https://link-toapi/' + id_Arr[i] + '/api-key-rest-of-link', (res) => { console.log('statusCode:', res.statusCode); console.log('headers:', res.headers); res.setEncoding('utf8'); let rawData = ''; res.on('data', (chunk) => { rawData += chunk; }); res.on('end', () => { try { const parsedDat = JSON.parse(rawData); race_arr.push(parsedDat); console.log(race_arr); } catch (e) { console.error(e.message); } }); }).on('error', (e) => { console.error(e); }); } var time = new Date() - start; console.log(time)
问题原因
核心问题是Node.js网络请求的异步特性+全局变量污染:
https.get是异步操作,发起第一个获取赛事列表的请求后,代码不会等待请求完成,直接执行后面的Get Live Circuit循环。此时第一个请求的end回调还没触发,id_Arr是空数组,id_Arr[i]实际是undefined,拼接到URL后变成https://link-toapi/undefined/api-key-rest-of-link,自然会提示端点不存在。- 你使用了全局变量(
i、arr等未用let/const限制作用域),后续循环的i会被第一个回调里的循环覆盖,进一步加剧时序混乱。
解决方案
方案1:将后续请求放到第一个请求的回调内部
确保只有在获取到赛事ID并填充到数组后,才发起后续请求:
const https = require('node:https'); let arr = []; let id_Arr = []; let race_arr = []; var start = new Date(); // Get Schedule https.get('link to api', (res) => { console.log('statusCode:', res.statusCode); console.log('headers:', res.headers); res.setEncoding('utf8'); let rawData = ''; res.on('data', (chunk) => { rawData += chunk; }); res.on('end', () => { try { const jsoData = JSON.parse(rawData); arr.push(jsoData.sport_events); const xm = arr[0].filter(item => item.status === 'live'); // 拿到ID后立即发起请求 for (let i = 0; i < xm.length; i++) { const eventId = xm[i].id; https.get(`https://link-toapi/${eventId}/api-key-rest-of-link`, (res) => { console.log('statusCode:', res.statusCode); console.log('headers:', res.headers); res.setEncoding('utf8'); let rawData = ''; res.on('data', (chunk) => { rawData += chunk; }); res.on('end', () => { try { const parsedDat = JSON.parse(rawData); race_arr.push(parsedDat); console.log(race_arr); } catch (e) { console.error(e.message); } }); }).on('error', (e) => { console.error(e); }); } } catch (e) { console.error(e.message); } }); }).on('error', (e) => { console.error(e); }); var time = new Date() - start; console.log(time)
方案2:用Promise封装请求+async/await(更清晰的异步控制)
将https.get封装成Promise,用async/await控制执行顺序,代码更易维护:
const https = require('node:https'); // 封装https.get为Promise function getRequest(url) { return new Promise((resolve, reject) => { https.get(url, (res) => { res.setEncoding('utf8'); let rawData = ''; res.on('data', (chunk) => { rawData += chunk; }); res.on('end', () => { try { resolve(JSON.parse(rawData)); } catch (e) { reject(e); } }); }).on('error', reject); }); } // 主逻辑用async函数 async function fetchLiveRaces() { let start = new Date(); try { // 1. 获取赛事列表 const scheduleData = await getRequest('link to api'); const liveEvents = scheduleData.sport_events.filter(item => item.status === 'live'); // 2. 并行发起所有赛事详情请求 const racePromises = liveEvents.map(event => getRequest(`https://link-toapi/${event.id}/api-key-rest-of-link`) ); const race_arr = await Promise.all(racePromises); console.log(race_arr); console.log(new Date() - start); } catch (e) { console.error(e.message); } } // 执行主函数 fetchLiveRaces();
额外优化点
- 用
let/const声明变量,避免全局变量污染; - 用模板字符串(
`xxx${var}xxx`)替代字符串拼接,更简洁; - 用
Array.map和Promise.all并行发起请求,提升效率。
内容的提问来源于stack exchange,提问作者almostasnecessary
相关产品推荐
相关产品推荐

