JS中的异步与回调
问题的引出:在js中使用异步调用时,有可能会出现在异步的回调函数中设置调用之外的变量值,但在异步调用完成后去使用变量,却发现这些变量值并没有被成功设置的情况。如: google map中的地理编码,地理编码是将地址(如“1600 Amphitheatre Parkway, Mountain View, CA”)转换为地理坐标(如纬度 37.423021 和经度 -122.083739)的过程,您可以根据该地理坐标放置标记或定位地图。这个服务是异步调用,他使用geocoder进行服务查询,在查询过程中会使用返回状态码,当返回成功后我们设置其中的经度和纬度,并在以后的过程中使用这些经度和纬度,当后面的程序使用这些经度和纬度的时候发现这些经度和纬度并没有被正确地设置。
例如:
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>Geocoding Test</title> 5 <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false"></script>
6 <script type="text/javascript" src="geocode.js"></script>
7 </head>
8 <body>
9 <div id="panel">
10 <input id="address" type="text" value="">
11 <input type="button" value="Change" onclick="codeAddress()">
12 </div> 13 <div id="map-canvas"></div> 14 </body> 15 </html>
1 var valLat = 0, valLng = 0; 2 3 function codeAddress() { 4 var geocoder = new google.maps.Geocoder(); 5 var address = document.getElementById('address').value; 6 7 geocoder.geocode({ 'address': address }, function (results, status) { 8 if (status == google.maps.GeocoderStatus.OK) { 9 valLat = results[0].geometry.location.lat(); 10 valLng = results[0].geometry.location.lng(); 11 } else { 12 alert('Geocode was not successful for the following reason: ' + status); 13 } 14 }); 15 alert("valLat: " + valLat + ", valLng: " + valLng); 16 }
整个页面有一个文本输入框和一个按扭,在文本框中输入地址“中国”,然后点击“Change”按扭
预期结果:显示出”中国“的经纬度信息
实际结果:显示的经纬度均为初始值0
分析原因:地理编码是异步服务,因此当执行geocode服务后就会继续执行后面处理经纬度的代码,此时的经纬度还没有被设置而是初始值
改进方法:将显示经纬度的代码放入地理编码的回调函数中,此时就会显示出正确的”中国“的经纬度
1 var valLat = 0, valLng = 0; 2 3 function codeAddress() { 4 var geocoder = new google.maps.Geocoder(); 5 var address = document.getElementById('address').value; 6 7 geocoder.geocode({ 'address': address }, function (results, status) { 8 if (status == google.maps.GeocoderStatus.OK) { 9 valLat = results[0].geometry.location.lat(); 10 valLng = results[0].geometry.location.lng(); 11 } else { 12 alert('Geocode was not successful for the following reason: ' + status); 13 } 14 alert("valLat: " + valLat + ", valLng: " + valLng); 15 }); 16 //alert("valLat: " + valLat + ", valLng: " + valLng); 17 }
新的问题:虽然这样可以解决异步调用显示不正确的问题,但是这样涉及到破坏封装,并造成不易于维护
改进方法:将涉及到经纬度处理的代码段使用一个函数进行调用
1 var valLat = 0, valLng = 0; 2 var count = 1; 3 4 //function codeAddress() { 5 function codeAddress(callback) { 6 var geocoder = new google.maps.Geocoder(); 7 var address = document.getElementById('address').value; 8 alert(address); 9 geocoder.geocode({ 'address': address }, function (results, status) { 10 if (status == google.maps.GeocoderStatus.OK) { 11 valLat = results[0].geometry.location.lat(); 12 valLng = results[0].geometry.location.lng(); 13 callback(valLat, valLng); 14 } else { 15 alert('Geocode was not successful for the following reason: ' + status); 16 } 17 //alert("valLat: " + valLat + ", valLng: " + valLng); 18 //alert("valLat: " + valLat + ", valLng: " + valLng + ", count: " + count); 19 20 }); 21 //alert("valLat: " + valLat + ", valLng: " + valLng); 22 }