为何console.log先于for循环执行?地理编码求经纬度均值遇异步问题
异步代码执行顺序问题分析
问题场景
这段JavaScript代码意图通过Geocode地理编码接口获取多个城市的经纬度并计算平均值,但运行后出现两个不符合预期的现象:
- 循环外的
console.log输出早于循环内的日志 - 最终计算出的经纬度均值全为0
代码示例
var holder = []; const compile = () =>{ let latitude = 0; let longitude = 0; for (let i = 0; i < holder.length; i++) { Geocode.fromAddress(holder[i].city).then( (response) => { const { lat, lng } = response.results[0].geometry.location; const foo = {lat, lng} longitude += Number(foo.lng); console.log(longitude) latitude += Number(foo.lat); console.log(latitude) }, (error) => { console.error(error); } ); } console.log("lat: " + latitude) latitude = latitude/holder.length; longitude = longitude/holder.length; console.log("lattitude: ", latitude, " longitude: ", longitude); }
运行输出
lat: 0 AddBox.js:49 lattitude: 0 longitude: 0 AddBox.js:53 -118.2436849 AddBox.js:39 34.0522342 AddBox.js:41 -192.7821738 AddBox.js:39 74.62067049999999
问题原因
核心问题是异步代码的执行逻辑特性:
Geocode.fromAddress是异步方法,调用后会立即返回一个Promise对象,JavaScript引擎不会等待接口请求完成,而是直接继续执行后续的同步代码(也就是循环外的console.log和均值计算)。- 只有当接口请求成功返回后,
then中的回调函数才会被触发执行。此时循环外的同步代码早已执行完毕,latitude和longitude还是初始值0,因此计算出的均值自然为0,且循环外的日志会先于回调内的日志输出。
修复方案
要等所有异步请求完成后再计算均值,可以使用Promise.all配合async/await来实现:
var holder = []; const compile = async () => { let latitude = 0; let longitude = 0; // 收集所有地理编码请求的Promise const geoPromises = holder.map(item => Geocode.fromAddress(item.city) .then(response => response.results[0].geometry.location) .catch(error => { console.error(error); // 出错时返回null,后续过滤无效结果 return null; }) ); // 等待所有异步请求完成 const locations = await Promise.all(geoPromises); // 过滤掉请求失败的结果 const validLocations = locations.filter(loc => loc !== null); // 累加有效经纬度 validLocations.forEach(({ lat, lng }) => { latitude += Number(lat); longitude += Number(lng); }); // 计算平均值(避免除以0) if (validLocations.length > 0) { latitude /= validLocations.length; longitude /= validLocations.length; } console.log("lat: " + latitude); console.log("lattitude: ", latitude, " longitude: ", longitude); }
方案说明
- 用
map将每个城市的地理编码请求转换为Promise,统一存入数组 - 借助
await Promise.all等待所有请求完成,一次性获取所有结果 - 过滤掉请求失败的结果,避免错误数据影响最终计算
- 用
async/await让异步代码逻辑更接近同步写法,可读性更强
内容的提问来源于stack exchange,提问作者Skiddzie
相关产品推荐
相关产品推荐

