Google Maps API脚本多维数组控制台验证后返回undefined问题排查
问题根源:异步操作的时序坑
你碰到的这个问题核心在于Google Maps地理编码API的请求是异步执行的,而你在$.post回调里的第二个for循环是同步跑的——当循环尝试访问markers[i][0]时,geocodeAddress里的地理编码请求还没完成,对应的markers[i]根本还没被赋值,自然会抛出TypeError: x[r] is undefined。
至于控制台能看到markers数组有6个元素,是因为浏览器控制台会实时更新引用类型的显示:你看到的是所有异步请求完成后的数组状态,而不是循环执行那一刻的状态,属于典型的“控制台欺骗”。
解决方案:等所有异步请求完成再处理标记
这里给你两种实用的解决思路,推荐第二种(Promise方案),更符合现代JS的写法:
方案1:用计数器追踪异步完成状态
在回调作用域内设置一个计数器,每次地理编码成功(或失败)后递增,当计数器等于预期的标记数量时,再执行处理markers的逻辑:
if ($('#propertymap').length){ var map=new google.maps.Map(document.getElementById('propertymap'), { center: {lat: 37.09024, lng: -95.712891}, zoom: 5 }); var bounds=new google.maps.LatLngBounds(); var geocoder=new google.maps.Geocoder(); var markers=[]; var infocontent=[]; // 新增计数器 var completedGeocodes = 0; var totalLocations; $.post('/_getproperties.php', {task:'getproperties'}, function(result){ var locationsSplit=result.replace('{', '').split('}'); totalLocations = locationsSplit.length - 1; for (var i=0; i<totalLocations; i++){ var currentFields=locationsSplit[i].split('|'); var id=currentFields[0]; var location=currentFields[1]; var street=currentFields[2]; var city=currentFields[3]; var state=currentFields[4]; var zip=currentFields[5]; var squarefootage=currentFields[6]; var ownedacreage=currentFields[7]; var leasedacreage=currentFields[8]; var hotelrooms=currentFields[9]; var website=currentFields[10]; geocodeAddress(location, street + ', ' + city + ', ' + state + ' ' + zip, location + '<br />' + street + '<br />' + city + ', ' + state + ' ' + zip, i); } }); function geocodeAddress(locationtitle, locationaddress, locationdescription, i){ geocoder.geocode({'address': locationaddress}, function(results, status){ if (status==='OK'){ markers[i]=[]; markers[i][0]=locationtitle; markers[i][1]=results[0].geometry.location.lat(); markers[i][2]=results[0].geometry.location.lng(); infocontent[i]=[]; infocontent[i][0]=locationdescription; } else { console.log('Geocode was not successful for the following reason: ' + status); } // 无论成功失败都递增计数器,避免无限等待 completedGeocodes++; // 所有异步请求完成后处理标记 if (completedGeocodes === totalLocations) { processMarkers(); } }); } // 封装处理标记的逻辑 function processMarkers() { console.log(markers); console.log(infocontent); for (var i=0; i<markers.length; i++){ console.log(i); console.log(markers[i][0]); // 这里还可以继续添加创建地图标记、绑定信息窗口的逻辑 } } }
方案2:用Promise.all管理所有异步请求
把每个地理编码请求包装成Promise,然后用Promise.all等待所有请求完成后再处理,代码结构更清晰:
if ($('#propertymap').length){ var map=new google.maps.Map(document.getElementById('propertymap'), { center: {lat: 37.09024, lng: -95.712891}, zoom: 5 }); var bounds=new google.maps.LatLngBounds(); var geocoder=new google.maps.Geocoder(); $.post('/_getproperties.php', {task:'getproperties'}, function(result){ var locationsSplit=result.replace('{', '').split('}'); var totalLocations = locationsSplit.length - 1; // 存储所有Promise var geocodePromises = []; for (var i=0; i<totalLocations; i++){ var currentFields=locationsSplit[i].split('|'); var location=currentFields[1]; var street=currentFields[2]; var city=currentFields[3]; var state=currentFields[4]; var zip=currentFields[5]; var locationaddress = street + ', ' + city + ', ' + state + ' ' + zip; var locationdescription = location + '<br />' + street + '<br />' + city + ', ' + state + ' ' + zip; // 把每个地理编码请求包装成Promise var promise = new Promise((resolve, reject) => { geocoder.geocode({'address': locationaddress}, function(results, status){ if (status==='OK'){ resolve({ title: location, lat: results[0].geometry.location.lat(), lng: results[0].geometry.location.lng(), info: locationdescription }); } else { reject('Geocode was not successful for the following reason: ' + status); } }); }); geocodePromises.push(promise); } // 等待所有Promise完成 Promise.all(geocodePromises) .then((markersData) => { // markersData是所有成功的地理编码结果数组 console.log(markersData); markersData.forEach((data, i) => { console.log(i); console.log(data.title); // 这里可以创建地图标记: // var marker = new google.maps.Marker({ // position: {lat: data.lat, lng: data.lng}, // map: map, // title: data.title // }); // 绑定信息窗口等逻辑 }); }) .catch((error) => { console.error(error); }); }); }
额外提示
- 注意Google Maps地理编码API有请求配额限制,批量请求时要避免触发限制;
- 你的
locationsSplit字符串分割逻辑比较脆弱,如果后端返回的格式有变化(比如多了空格)就容易出错,建议后端直接返回JSON格式,前端用JSON.parse解析,比字符串分割靠谱得多。
内容的提问来源于stack exchange,提问作者BB Design
相关产品推荐
相关产品推荐

