写在前面
今天也是写微信小程序的一天,真真切切的体会到了什么叫做异步。之前写其他代码,从来没考虑过什么异步,同步的,也基本不用不到。而在微信小程序中不得不考虑了。毕竟 文档 中指明了:
小程序是基于双线程模型,那就意味着任何数据传递都是线程间的通信,也就是都会有一定的延时。这不像传统Web那样,当界面需要更新时,通过调用更新接口 UI 就会同步地渲染出来。在小程序架构里,这一切都会变成异步。
除了逻辑层与渲染层之间的通信有延时,各层与客户端原生交互同样是有延时的。以逻辑层为例,开发者的代码是跑在逻辑层这个线程之上,而客户端原生是跑在微信主线程(安卓上是线程)之上,所以注册给逻辑层有关客户端能力的接口,实际上也是跟微信主线程之间的通信,同样意味着有延时。这也是我们看到大部分提供的接口都是异步的原因。
小插入
使用了一个 map 原生组件差点心态爆炸。页面加载后,视觉中心竟然不是自己当前位置,而是大老远的北京。最后发现所谓视觉中心原来是指 map 中的属性 longitude
和 latitude
。
官方示例
<map id="map" longitude="113.324520" latitude="23.099994" scale="14" controls="{{controls}}" bindcontroltap="controltap" markers="{{markers}}" bindmarkertap="markertap" polyline="{{polyline}}" bindregionchange="regionchange" show-location style="width: 100%; height: 300px;"></map>
更多属性说明查阅 文档
我的代码
<map id="map" scale="14" controls="{{controls}}" bindcontroltap="controltap" markers="{{markers}}" bindmarkertap="markertap" bindregionchange="regionchange" show-location style="width: 100%; height: 950rpx;" bindtap="getloc"></map>
自己没有设置 longitude
和 latitude
的属性值,结果它给我默认为longitude="113.324520" latitude="23.099994"
。也就是北京的经纬度。我寻思着文档中也没说默认值是多少,而且还是必填属性,怎么我没有设置,也不给我报错呢(/哭笑)。它既然可以标注我的位置,为什么不干脆把视觉中心设置成当前位置(/哭笑)。惹不起,还是要自己调 wx.getLocation
接口获取当前位置。像这样:
wxml
中
<map id="map" scale="16" longitude="{{longitude}}" latitude="{{latitude}}" controls="{{controls}}" bindcontroltap="controltap" markers="{{markers}}" bindmarkertap="markertap" bindregionchange="regionchange" show-location style="width: 100%; height: 950rpx;" bindtap="getloc"></map>
JS 代码片段
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (option) {
var that = this
console.log('---onLoad---')
// 获取用户位置
wx.getLocation({
type: 'gcj02',
success:res=> {
console.log('---getLocation success---')
const latitude = res.latitude
const longitude = res.longitude
const speed = res.speed
const accuracy = res.accuracy
that.setData({
latitude: latitude,
longitude: longitude
})
},
fail:res=> {
console.log('---getLocation fail---')
},
complete:res=> {
console.log('---getLocation complete---')
}
})
console.log('---onLoad over---')
},
异步
百度词条
异步双方不需要共同的时钟,也就是接收方不知道发送方什么时候发送,所以在发送的信息中就要有提示接收方开始接收的信息,如开始位,同时在结束时有停止位。
异步的另外一种含义是计算机多线程的异步处理。与同步处理相对,异步处理不用阻塞当前线程来等待处理完成,而是允许后续操作,直至其它线程将处理完成,并回调通知此线程。
前面也提到了,微信小程序中大部分提供的接口是异步的。这也就是为什么许多接口中都提供了这样的方法:success
fail
以及 complete
。显而易见,上面的 wx.getLocation
也是异步接口。为了便于更直观的调试,上面的 JS 代码中添加了四个 console.log
语句。运行后可以在控制台看见这样的输出信息(一部分):
...省略···
---onLoad---
---onLoad over---
...省略···
---onReady---
---getLocation success---
---getLocation complete---
...省略···
这样的输出很直观的体会到异步的概念。console.log('---onLoad over---')
在 wx.getLocation
后面却先执行完毕了。甚至在小程序页面周期进入 onReady了,wx.getLocation
才执行完毕。
wx.getLocation
获取当前的地理位置、速度。当用户离开小程序后,此接口无法调用。开启高精度定位,接口耗时会增加,可指定 highAccuracyExpireTime 作为超时时间
这是因为当遇到一个耗时的 异步 API 时,并不会阻塞当前线程,wx.getLocation
本身会在在另一个线程里执行,所以就先执行后面的程序了。而提供 success
fail
以complete
这样的方法恰恰就是方便我们对其进行监控。
注意
对异步接口有了一些了解后,在调用接口时就不得考虑一些逻辑先后问题了。例如这样修改上面的那段代码:
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (option) {
var that = this
console.log('---onLoad---')
// 获取用户位置
wx.getLocation({
type: 'gcj02',
success:res=> {
console.log('---getLocation success---')
const latitude = res.latitude
const longitude = res.longitude
const speed = res.speed
const accuracy = res.accuracy
that.setData({
latitude: latitude,
longitude: longitude
})
},
fail:res=> {
console.log('---getLocation fail---')
},
complete:res=> {
console.log('---getLocation complete---')
}
})
// 改变视觉中心
if(option.lat != null){
that.setData({
latitude: option.lat,
longitude: option.lng,
})
}
consolo.log(that.data.latitude)
},
假设这里 data 中的 longitude
和 latitude
初始值都是 null。当有一个类似这样的页面跳转时:
wx.navigateTo({
url: '../index/index?lat='+lat+'&lng='+lng,
})
我们期待跳转成功后,页面的视觉中心变成位置 lat
lng
处。但是运行上面的代码会发现,地图中心的位置依旧在当前位置。控制台输出的 latitude 也正是当前位置的。明显的,传入进来的 lat 和 lng ,被 wx.getLocation 返回的值覆盖了。 所以当有类似这样的需求时,我们不得不把后面的代码放在 complete
方法里,以确保这段代码在 wx.getLocation 之后执行。像这样:
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (option) {
var that = this
console.log('---onLoad---')
// 获取用户位置
wx.getLocation({
type: 'gcj02',
success:res=> {
console.log('---getLocation success---')
const latitude = res.latitude
const longitude = res.longitude
const speed = res.speed
const accuracy = res.accuracy
that.setData({
latitude: latitude,
longitude: longitude
})
},
fail:res=> {
console.log('---getLocation fail---')
console.log(res)
},
complete:res=> {
// 改变视觉中心
if(option.lat != null){
that.setData({
latitude: option.lat,
longitude: option.lng,
})
}
console.log('---getLocation complete---')
}
})
consolo.log(that.data.latitude)
},
I know nothing but my ignorance.