函数未返回预期值:外部访问stations数组为空的技术问询
问题分析与解决:异步请求导致的数组获取异常
问题重现
你使用request库获取CSV文件并转换为对象数组,回调内部console.log(stations)能得到预期数据,但外部执行console.log(stations)却返回空数组,同时无法通过返回值获取数组。你的代码如下:
const translation = { '緯度': 'latitude', '経度': 'longitude', '測定局コード': 'Measuring station code', '測定局名称': 'Bureau name', '所在地': 'location', '測定局種別': 'Measuring station type', '問い合わせ先': 'Contact information', '都道府県コード': 'Prefecture code' } const csv_url = 'https://soramame.env.go.jp/data/map/kyokuNoudo/2022/10/06/01.csv'; var stations = []; request(csv_url, (err, res, body) => { if (err || res.statusCode !== 200) { console.log(err); } else { const rows = body.split('\n'); const headers = rows[0].split(','); for (let i = 1; i < rows.length; i++) { // start at 1 to skip headers let station = {}; // create a new station object for each row const row = rows[i].split(','); // split the row into columns for (let j = 0; j < row.length; j++) { // loop through each column station[translation[headers[j]]] = row[j]; // add the value to the station object by using the header as the key } var data = stations.push(station) console.log(stations) return data; } } });
核心原因
异步执行机制:
request是异步函数,代码执行顺序为:- 先声明空数组
stations - 调用
request发起网络请求,此时JS引擎会继续执行后续代码(比如外部的console.log(stations)) - 只有当网络请求完成后,回调函数才会执行并填充
stations数组
因此外部console.log执行时,回调还未触发,数组自然为空。
- 先声明空数组
循环逻辑错误:回调内的
return data;放在循环体内部,会导致循环仅执行一次就退出,根本无法生成完整的对象数组。
解决方案
方案1:用Promise+async/await处理异步(推荐)
将异步请求封装为Promise,通过async/await同步化代码逻辑,避免回调嵌套:
const translation = { '緯度': 'latitude', '経度': 'longitude', '測定局コード': 'Measuring station code', '測定局名称': 'Bureau name', '所在地': 'location', '測定局種別': 'Measuring station type', '問い合わせ先': 'Contact information', '都道府県コード': 'Prefecture code' } const csv_url = 'https://soramame.env.go.jp/data/map/kyokuNoudo/2022/10/06/01.csv'; // 封装request为Promise function fetchCSV(url) { return new Promise((resolve, reject) => { request(url, (err, res, body) => { if (err || res.statusCode !== 200) { reject(err || new Error(`请求失败,状态码:${res.statusCode}`)); } else { resolve(body); } }); }); } // 异步函数处理CSV解析 async function getStations() { try { const body = await fetchCSV(csv_url); const stations = []; const rows = body.split('\n'); const headers = rows[0].split(','); for (let i = 1; i < rows.length; i++) { // 跳过空行,避免生成无效对象 if (!rows[i].trim()) continue; let station = {}; const row = rows[i].split(','); for (let j = 0; j < row.length; j++) { station[translation[headers[j]]] = row[j]; } stations.push(station); } console.log(stations); // 完整对象数组 return stations; } catch (err) { console.error(err); } } // 调用函数并处理结果 getStations().then(stations => { // 在这里编写依赖stations的业务逻辑 console.log('外部获取到的stations:', stations); });
方案2:将业务逻辑放在回调内部
如果不想使用Promise,直接将需要用到stations的逻辑放在回调函数内部,确保数据填充完成后再执行:
const translation = { '緯度': 'latitude', '経度': 'longitude', '測定局コード': 'Measuring station code', '測定局名称': 'Bureau name', '所在地': 'location', '測定局種別': 'Measuring station type', '問い合わせ先': 'Contact information', '都道府県コード': 'Prefecture code' } const csv_url = 'https://soramame.env.go.jp/data/map/kyokuNoudo/2022/10/06/01.csv'; // 定义处理stations的业务函数 function handleStations(stations) { console.log('处理后的stations:', stations); // 在这里添加你的业务逻辑 } request(csv_url, (err, res, body) => { if (err || res.statusCode !== 200) { console.log(err); return; } const stations = []; const rows = body.split('\n'); const headers = rows[0].split(','); for (let i = 1; i < rows.length; i++) { if (!rows[i].trim()) continue; let station = {}; const row = rows[i].split(','); for (let j = 0; j < row.length; j++) { station[translation[headers[j]]] = row[j]; } stations.push(station); } // 数据填充完成后执行业务逻辑 handleStations(stations); });
关键注意点
- 必须删除原代码中循环内的
return data;,否则循环仅执行一次,无法生成完整数组。 - 处理CSV时注意跳过空行,避免生成无效的空对象。
内容的提问来源于stack exchange,提问作者Germain Campman
相关产品推荐
相关产品推荐

