JavaScript调用Google地理定位REST API,回调外无法获取数据求助
问题根源与解决方案
你的代码本身没有语法错误,问题出在异步操作的执行顺序上!
当你调用xmlhttp.open('POST',url,true)时,第三个参数true表示这是一个异步请求。浏览器发送请求后,不会等着API返回结果,而是直接继续执行后面的代码——也就是你标注的[2 operation],这时候lat和lng还没被赋值,自然是null。而onreadystatechange回调函数要等API请求成功返回数据后才会执行,也就是你标注的[1 operation],这时候才能拿到有效的经纬度值。
下面给你几个可行的解决方案:
方案1:把业务逻辑放在回调函数内部
最直接的方式,就是把需要使用lat和lng的代码都放到onreadystatechange的成功回调里,或者封装成函数在回调里调用:
var postdata = '{"cellTowers":[{"cellId": '+cid+',"locationAreaCode": '+lac+',"mobileCountryCode": '+mcc+',"mobileNetworkCode": '+mnc+'}]}' xmlhttp = new XMLHttpRequest(); var url = "https://www.googleapis.com/geolocation/v1/geolocate?key=YOURKEY"; xmlhttp.open('POST',url,true); xmlhttp.setRequestHeader("Content-Type", "application/json"); xmlhttp.send(postdata); var lat; var lng; xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { if (xmlhttp.responseText) { var data = xmlhttp.responseText; var json = JSON.parse(data); lat = json.location.lat; lng = json.location.lng; // 把需要用经纬度的逻辑放在这里,或者调用一个处理函数 handleLocation(lat, lng); } } }; // 封装处理逻辑的函数 function handleLocation(latitude, longitude) { alert(latitude + " " + longitude); // 这里能正常拿到值 // 其他需要用经纬度的操作都可以在这里写 }
方案2:用Promise封装异步请求
如果不想把所有逻辑都塞进回调里,可以把XMLHttpRequest包装成Promise,用更清晰的链式调用或者async/await来处理:
function getLocation(cid, lac, mcc, mnc) { return new Promise((resolve, reject) => { var postdata = `{"cellTowers":[{"cellId": ${cid},"locationAreaCode": ${lac},"mobileCountryCode": ${mcc},"mobileNetworkCode": ${mnc}}]}`; const xmlhttp = new XMLHttpRequest(); const url = "https://www.googleapis.com/geolocation/v1/geolocate?key=YOURKEY"; xmlhttp.open('POST', url, true); xmlhttp.setRequestHeader("Content-Type", "application/json"); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4) { if (xmlhttp.status == 200) { const json = JSON.parse(xmlhttp.responseText); resolve({lat: json.location.lat, lng: json.location.lng}); } else { reject(new Error('请求失败:' + xmlhttp.status)); } } }; xmlhttp.send(postdata); }); } // 调用方式:链式调用 getLocation(cid, lac, mcc, mnc) .then(location => { alert(location.lat + " " + location.lng); // 这里可以处理拿到位置后的逻辑 }) .catch(error => { console.error(error); }); // 或者用async/await(需要在async函数里使用) async function fetchLocation() { try { const location = await getLocation(cid, lac, mcc, mnc); alert(location.lat + " " + location.lng); } catch (error) { console.error(error); } } fetchLocation();
方案3:使用jQuery的$.ajax方法
既然你提到了jQuery,它的$.ajax方法本身就封装了异步请求,支持Promise风格,代码会更简洁:
var postdata = { cellTowers: [{ cellId: cid, locationAreaCode: lac, mobileCountryCode: mcc, mobileNetworkCode: mnc }] }; $.ajax({ url: "https://www.googleapis.com/geolocation/v1/geolocate?key=YOURKEY", type: "POST", contentType: "application/json", data: JSON.stringify(postdata), success: function(response) { const lat = response.location.lat; const lng = response.location.lng; alert(lat + " " + lng); // 处理位置逻辑 }, error: function(xhr, status, error) { console.error('请求失败:', error); } }); // 或者用Promise风格的$.ajax $.ajax({ url: "https://www.googleapis.com/geolocation/v1/geolocate?key=YOURKEY", type: "POST", contentType: "application/json", data: JSON.stringify(postdata) }) .then(response => { const lat = response.location.lat; const lng = response.location.lng; alert(lat + " " + lng); }) .catch(error => { console.error(error); });
总结一下:异步请求的核心就是不要在请求未完成时去拿返回值,必须等请求成功的回调触发后,再处理返回的数据。上面的几种方案都能解决你的问题,你可以根据自己的代码风格选择合适的方式。
内容的提问来源于stack exchange,提问作者13hola
相关产品推荐
相关产品推荐

