
当你在控制台看到数组非空(如 [] 下展开显示子项),但 JSON.stringify() 返回空数组、访问 arr[0] 得到 undefined,根本原因通常是:数组被正确填充,但你尝试读取时它尚未完成异步填充——即读取发生在回调执行前,属于典型的“竞态条件”问题。
当你在控制台看到数组非空(如 [] 下展开显示子项),但 json.stringify() 返回空数组、访问 arr[0] 得到 undefined,根本原因通常是:**数组被正确填充,但你尝试读取时它尚未完成异步填充——即读取发生在回调执行前,属于典型的“竞态条件”问题。**
问题本质:异步加载 vs 同步访问
你的 L.GPX 插件通过 XMLHttpRequest 异步加载 GPX 文件,并在 req.onreadystatechange 回调中解析数据、调用 _addGPX → parse_trkseg → 最终执行 Gpx_Array.push(coords)。这意味着:
- Gpx_Array.push(...) 只在 XML 加载完成、DOM 解析完毕后才触发;
- 而你在主逻辑中紧随 new L.GPX(...) 之后立即 console.log(Gpx_Array) 或 Gpx_Array[0] —— 此时请求可能刚发出,甚至尚未返回,数组仍是空的;
- 浏览器开发者工具的 console.log(Gpx_Array) 显示展开后的结构,是因为它延迟求值(lazy evaluation):点击展开时才真正读取当前值,此时异步回调早已执行完毕,所以能看到内容;而 JSON.stringify() 是立即序列化,此时数组仍为空。
✅ 这就是为什么 console.log(Gpx_Array) 看似有数据,但 JSON.stringify(Gpx_Array) 和 Gpx_Array[0] 却是空或 undefined。
正确解决方案:基于事件或 Promise 的同步协调
不要依赖全局变量 + 手动计数 + localStorage(低效且不可靠),而是利用插件已有的事件机制:
✅ 推荐方案:监听 'loaded' 事件(最优雅)
L.GPX 在数据加载并解析完成后会触发 'loaded' 事件。你应该在此事件回调中安全访问 Gpx_Array:
// 声明全局或模块级数组(确保作用域可访问)
let Gpx_Array = [];
// 初始化 GPX 图层
const track = new L.GPX('tracce_gpx/Ovaro(MonteZoncolan)_Sutrio.gpx', {
async: true,
display_wpt: false,
color: '#f5f5de',
opacity: 0.4,
weight: 6
});
// 关键:在 'loaded' 事件中读取数据
track.on('loaded', function() {
console.log('GPX fully loaded!');
console.log('Gpx_Array length:', Gpx_Array.length); // ✅ 此时必为预期值
console.log('First segment:', Gpx_Array[0]); // ✅ 可安全访问
console.log('JSON:', JSON.stringify(Gpx_Array, null, 2)); // ✅ 正常输出
// ✅ 在此处执行所有依赖 GPX 数据的逻辑
// 例如:渲染额外图表、计算路径统计、导出坐标等
});
// 将图层添加到地图(注意:事件监听必须在 addLayer 之前或之后均可,但需在 loaded 触发前绑定)
track.addTo(Video);
同时,修改你的 parse_trkseg 方法,将 coords 推入 Gpx_Array(你注释掉了这行):
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
parse_trkseg: function (line, xml, options, tag) {
// ... 原有解析逻辑 ...
var coords = [];
for (var i = 0; i <h4>⚠️ 注意事项</h4>
-
避免全局污染:Gpx_Array 应声明在闭包或模块作用域内,而非挂载到 window。若需多实例支持,可将数组作为 L.GPX 实例属性:
initialize: function (gpx, options) { L.Util.setOptions(this, options); this._gpx = gpx; this._layers = {}; this.gpxSegments = []; // ✅ 实例专属数组 if (gpx) { this.addGPX(gpx, options, this.options.async); } }, // 在 parse_trkseg 中使用:this.gpxSegments.push(coords); localStorage 方案为何不推荐?
localStorage.setItem(key, value) 会自动将 value 转为字符串(调用 toString()),对数组会变成 "[object Object],[object Object],...",丢失全部结构和嵌套属性。你当前能“工作”是因为浏览器调试时复制对象是深拷贝快照,并非 localStorage 真正存下了可用数据——后续 getItem() 返回的是无意义字符串,无法 JSON.parse() 恢复。不要用 setTimeout 或轮询:这是反模式,既不可靠又损害性能。
总结
| 现象 | 根本原因 | 正确做法 |
|---|---|---|
| console.log(arr) 展开可见内容,但 arr[0] 为 undefined | 异步加载未完成,读取过早 | 在 'loaded' 事件回调中访问 |
| JSON.stringify(arr) 返回 [] | 序列化发生在填充前 | 同上,等待事件触发 |
| 依赖 localStorage 临时存储 | 数据被强制转为字符串,结构丢失 | 使用实例属性 + 事件驱动 |
遵循事件驱动范式,不仅能解决当前问题,还能让代码更健壮、可维护、符合现代 JavaScript 异步编程规范。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










