
本文详解 d3 v7 中 `d3.csv()` 的异步特性导致全局数组在函数外仍为 `undefined` 的根本原因,并提供基于 `async/await` 的标准解决方案,确保数据加载完成后再执行地图标记等依赖操作。
D3 v7 的 d3.csv() 返回一个 Promise,而非同步填充数据——这是问题的核心。您声明了全局数组(如 projectName = []),并在 data() 函数中尝试赋值,但由于未等待 Promise 解析完成,createMarkers() 就已执行,此时全局数组仍是空的(访问 lat[z] 自然得到 undefined,进而导致 NaN 或报错)。
要修复此问题,必须让 initMap() 等待 CSV 加载完毕。关键修改有三处:
- data() 函数需返回 d3.csv() Promise,并统一使用现代箭头函数解析器(避免旧式回调嵌套);
- initMap() 必须 await data(),确保全局数组已填充;
- createMarkers() 需接收 map 实例作为参数(因 map 在 initMap() 内定义,非全局)。
以下是修正后的完整代码(含健壮性增强):
// 请求 Google Maps 库
const { Map, InfoWindow } = await google.maps.importLibrary("maps");
const { LatLng } = await google.maps.importLibrary("core");
const { AdvancedMarkerElement, PinElement } = await google.maps.importLibrary("marker");
// 全局数组(保持声明)
let projectName = [];
let artist = [];
let lat = [];
let long = [];
let markers = [];
// ✅ 修正:data() 返回 Promise,使用 d3.csv 的现代解析方式
async function data() {
return d3.csv("/data/single.csv", (d) => ({
projectName: d.Project_Name, // 字符串,勿用 +d.Project_Name(会转 NaN)
artist: d.Artist,
lat: parseFloat(d.Latitude), // 显式 parseFloat 更安全
long: parseFloat(d.Longitude),
}));
}
// ✅ 修正:createMarkers 接收 map 参数,避免作用域问题
function createMarkers(m) {
for (let z = 0; z < lat.length; z++) {
if (isNaN(lat[z]) || isNaN(long[z])) continue; // 跳过无效坐标
markers.push(
new google.maps.Marker({
position: { lat: lat[z], lng: long[z] }, // 直接传对象,更简洁
map: m,
title: projectName[z] || "Unknown Project",
})
);
}
}
// ✅ 修正:initMap 使用 async/await,确保顺序执行
async function initMap() {
const map = new Map(document.getElementById("map"), {
zoom: 11,
center: { lat: 37.4239163, lng: -122.0947209 },
mapId: "[MAP ID]",
mapTypeControl: false,
});
try {
// ? 关键:等待 CSV 加载并填充全局数组
const csvData = await data();
// 填充全局数组(推荐:直接赋值,避免手动循环索引)
projectName = csvData.map(d => d.projectName);
artist = csvData.map(d => d.artist);
lat = csvData.map(d => d.lat);
long = csvData.map(d => d.long);
// 创建标记
createMarkers(map);
} catch (error) {
console.error("CSV 加载失败:", error);
alert("地图数据加载异常,请检查 CSV 文件路径和格式。");
}
}
initMap();注意事项与最佳实践:
- ❌ 避免在 d3.csv 回调中混用旧式 function(d){} 和 +d.xxx 类型转换(数字字段缺失时易得 NaN);✅ 推荐用 parseFloat() 并配合 isNaN() 校验;
- ❌ 不要将 map 声明为全局变量(违反封装原则且易引发冲突);✅ 通过参数传递更清晰、可测试;
- ✅ 使用 try/catch 捕获 CSV 加载异常(如 404、格式错误),提升应用鲁棒性;
- ✅ 优先用 csvData.map() 批量赋值,比手动 for 循环更简洁、不易越界。
遵循以上模式,即可彻底解决“全局数组在异步加载后仍为 undefined”的问题,确保地理数据与地图渲染的严格时序依赖。










