前言
最近碰到一个需求,就是将我们系统中要导出一系列的工作数据,这里面有一个需求就是根据工作人员的经纬度去获取他可能的位置。说到这里大家肯定能想到用地图API了,我这里用的是百度地图的API(详情可参考)。然后这里就涉及到一个问题,工作数据是很多条的,但是这里的API很明显就只能一条一条的去请求,那么我们怎么封装能够达到我们的目的呢。
获取百度AK
首先呢,我们想要使用百度地图相关的API的话,肯定是要去注册申请相关的使用资格的,详细的账号申请流程呢,请戳这里,这里面详细介绍了如何注册并申请百度AK,只有拿到了AK,我们在使用相关的API时才会有效,否则是不生效的话,所以大家一定要保证有AK并且是有效的哈。
两种逆地址解析的方法
申请好了AK之后呢,这里有两种使用方法:
一种是直接通过url来进行请求,像是下边这种
http://api./geocoder?location=39.910093,116.403945&output=json&key=你的百度AK
复制代码
这种方法使用的话,大家可以直接把这个url放到axios或者ajax里面,然后他的回调里面返回的就是我们想要的地址等相关信息了。另一种方法就是官网里面介绍的方法:
// 初始化地图,这里的window可能需要从父组件传过来,原理还没弄清楚
var map = new window.BMap.Map("allmap");
// 创建地理编码实例
var myGeo = new window.BMap.Geocoder();
// 根据坐标得到地址描述
myGeo.getLocation(new window.BMap.Point(116.404, 39.915), function(result){
if (result){
console.log('这就是解析之后的地址了',resule.address)
}
})
复制代码注意:用第二种方法的话,我们需要在index.html里面引用我们上面的AK,如下
复制代码
上面的两种方法,都可以达到我们获取地址信息的目的哈。我这里选用的是第二种方法,然后后面我们批量请求的话,也是针对第二种方式来进行改装的。
问题分析
刚开始我的思路是想着,既然这种方法只能够一条一条请求的话,那我就将经纬度构建成一个数组,通过循环调用的方法来给这个数组中的值赋值,代码如下
gpsDatas.forEach((gpsItem)=>{
var map = new window.BMap.Map("allmap");
// 创建地理编码实例
var myGeo = new window.BMap.Geocoder();
// 根据坐标得到地址描述,将每一项的经纬度传入
myGeo.getLocation(new window.BMap.Point(gpsItem.longitude, gpsItem.latitude), function(result:any){
if (result){
// 将返回的值赋给该字段
gpsItem.maybePosition = result.address
}
})
})
// 返回我们要输出的数据
console.log('gpsDatas',gpsDatas)
console.log('gpsDatas[0]',gpsDatas[0].maybePosition)
let a = gpsDatas[0].maybePosition
return gpsDatas
复制代码
如果大家执行到了这里的话,我们就会发现一个很经典的问题,也是我们经常可能会碰到的问题,就是我们可以看到第一个打印出来的gpsDatas数组里面是能够看到我们拿到了我们想要的值,但是第二个打印我们可能就会看到一个undefined了,那么有小伙伴就可能会产生这样一个疑惑了,为什么我明明打印出来看有这个值,但是为什么取不到值呢,这是因为在我们取值的时候,myGeo.getLocation这个异步函数还没有执行完毕,所以取值的时候我们是取不到这个值的,但是因为gpsDatas是一个引用数据类型,所以在异步函数执行完毕后,他会把数据填充到这个数组里面,因为引用地址没有改变,所以我们可以看到数组中的地址已经有值了。
可以这样理解,console输出数据的时候,他输出引用数据类型和基本数据类型是不同的。输出引用类型的话,其实他是输出了一个指针,我们看到的就是指向内存中的一片地址中的数据,所以尽管他是后边异步把数据放上来的,但是因为空间指向没有改变,所以输出的数据其实一直是在改变的,不过是console没有把这一步表现出来而已。
那么很显然,现在这种情况肯定是不满足我们的需求的,那么这里我们就需要将我们的方法就行改装一下了。
最终解决方案
经过前面的分析,我们可知,问题的产生是因为我们是一个循环调用异步函数的问题,循环先走完了,但是异步没有走完,所以导致我们取不出数据,那么大家其实很容易想到Promise,我们可以利用Promise.all,将所有的异步操作一起执行,然后在.then里面获取返回结果。实现如下:
getMaybePositionByLaLo(window,positionDatas){
// 定义一个Promise数组,来将异步操作放进来
let apiDatas = []
positionDatas.forEach((gpsItem)=>{
let apiItem = this.getMapInfo(window,gpsItem)
apiDatas.push(apiItem)
})
// 这里.all会将所有的异步操作一起放在队列中,等待所有异步执行完毕后才会执行.then,这就保证了我们的同步获取数据
return Promise.all(apiDatas)
.then((gpsData)=>{
return gpsData
})
}
// 将单个获取地理位置的方法封装成一个方法
// 这里传入window的目的是,有些地方直接new BMap会报错,需要new window.BMap才可以
getMapInfo(window,gpsItem){
return new Promise((resolve, reject)=>{
var map = new window.BMap.Map("allmap");
// 创建地理编码实例
var myGeo = new window.BMap.Geocoder();
// 根据坐标得到地址描述
myGeo.getLocation(new window.BMap.Point(gpsItem.longitude, gpsItem.latitude), function(result){
if (result){
gpsItem.maybePosition = result.address;
resolve(gpsItem)
}
})
})
}
// 然后我们就可以在页面直接调用了
this.getMaybePositionByLaLo(window,positionDatas)
.then((positionDatas)=>{
// 这里就可以去进行我们的取值赋值操作了
})
复制代码
以上我们就完成了我们的批量改装,其实核心就是对于Promise的应用,如何合理的运用Promise,对于我们以后的开发有着很大的好处,我也是最近才开始发现Promise的好处,之前只是了解,也没有多的去使用,欢迎大家以后一起学习指正。