You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

函数未返回预期值:外部访问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;
        }
    }
});

核心原因

  1. 异步执行机制:request是异步函数,代码执行顺序为:

    • 先声明空数组stations
    • 调用request发起网络请求,此时JS引擎会继续执行后续代码(比如外部的console.log(stations))
    • 只有当网络请求完成后,回调函数才会执行并填充stations数组
      因此外部console.log执行时,回调还未触发,数组自然为空。
  2. 循环逻辑错误:回调内的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 23:45:40