Node.js中异步操作外部打印数组返回空的问题求助
问题原因分析
你遇到的这个问题是Node.js异步非阻塞特性导致的。简单来说,你代码里的request回调和rp(options).then()都是异步任务——Node.js会把这些任务放到事件队列里,先继续执行主线程的同步代码(也就是最后那行console.log(awayPerc))。这时候异步任务还没开始执行,数组自然是空的;而在异步回调内部打印时,回调已经执行完毕,数组已经被填充,所以能看到正常数据。
解决方案:用Promise统一异步流程
为了保证在所有异步数据都获取完成后再访问数组,我们可以把回调式的request包装成Promise,然后用Promise.all或者async/await来等待所有异步操作结束,再处理数据。这样还能避免使用全局变量,让代码更清晰可控。
步骤1:把request请求包装成Promise
const rp = require('request-promise'); const cheerio = require('cheerio'); const request = require('request'); // 封装获取资金分布数据的Promise函数 const getMoneyDistribution = () => { return new Promise((resolve, reject) => { request('https://www.actionnetwork.com/mlb/live-odds', function (err, res, html) { if (err || res.statusCode !== 200) { return reject(err || new Error('Failed to fetch money distribution data')); } const $ = cheerio.load(html); const awayPerc = []; const homePerc = []; $('td.text-right.border-left span:first-child').each(function(i, element) { const awayPercs = $(this).text(); const homePercs = $(this).next().text(); awayPerc.push({ awayPercs }); homePerc.push({ homePercs }); }); resolve({ awayPerc, homePerc }); }); }); };
步骤2:封装API请求为Promise函数(优化原rp代码)
// 封装获取比赛数据的Promise函数 const getGameData = () => { const options = { url: "https://api-prod.sprtactn.co/web/v1/scoreboard/mlb?date=20180520", json: true }; return rp(options).then(data => { const homeTeam = []; const awayTeam = []; const openOdds = []; const currOdds = []; const games = data.games; // 注意:原代码变量名重复容易混淆,这里将循环参数改为game games.forEach(game => { game.teams.forEach(team => { if (game.home_team_id === team.id) { homeTeam.push({ homeTeam: team.full_name }); } else if (game.away_team_id === team.id) { awayTeam.push({ awayTeam: team.full_name }); } }); game.odds.forEach(odds => { if (odds.type === "game" && odds.book_id === "15") { currOdds.push({ curAway: odds.ml_away, curHome: odds.ml_home }); } else if (odds.type === "game" && odds.book_id === "30") { openOdds.push({ openAway: odds.ml_away, openHome: odds.ml_home }); } }); }); return { homeTeam, awayTeam, openOdds, currOdds }; }); };
步骤3:用async/await等待所有异步任务完成
// 主函数:获取所有数据并处理 async function fetchAndProcessData() { try { // 并行执行两个异步请求,等待两者都完成 const [gameData, moneyData] = await Promise.all([getGameData(), getMoneyDistribution()]); // 解构获取所有数据 const { homeTeam, awayTeam, openOdds, currOdds } = gameData; const { awayPerc, homePerc } = moneyData; // 现在可以正常访问所有填充好的数据了! console.log('Away Team Money Distribution:', awayPerc); console.log('Home Teams:', homeTeam); console.log('Current Odds:', currOdds); // 这里可以添加数据合并、存储等后续逻辑 } catch (err) { console.error('Error fetching data:', err); } } // 启动主函数 fetchAndProcessData();
关键注意事项
- 避免全局变量:全局变量在异步场景下很容易出现时序问题,把数据封装在Promise返回值里更安全。
- 异步任务的执行顺序:
Promise.all会并行执行多个异步任务,比顺序执行效率更高;如果某个任务依赖另一个的结果,可以把await放在顺序位置。 - 错误处理:一定要用
try/catch或者.catch()捕获异步操作中的错误,避免程序崩溃。
内容的提问来源于stack exchange,提问作者G. Boyce
相关产品推荐
相关产品推荐

