问题1:
在微信小程序开发过程中,在获取userinfo或其他异步处理函数的过程中采用this碰到一些问题,问题如下:
我的login的success回调函数如下:
success: function(res) {
if(res.statusCode == 200) {
console.log(res.data)
this.globalData.sessionId = res.data.sessionId
}
运行时,此处的this会报错如下:
Cannot read property 'globalData' of undefined;at App getData function;at api operateWXData success callback function
具体截图如下图所示:
然后我查看了app.js另外一处的success回调函数
success: res => {
// 可以将 res 发送给后台解码出 unionId
this.globalData.userInfo = res.userInfo
// 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
// 所以此处加入 callback 以防止这种情况
if (this.userInfoReadyCallback) {
this.userInfoReadyCallback(res)
}
而此处的this有效,我怀疑是success: function(res) {} 和 success: res => {}的区别,于是我修改了第一处的代码为:
success: res => {
if(res.statusCode == 200) {
console.log(res.data)
this.globalData.sessionId = res.data.sessionId
}
执行成功!如果不想修改回调函数,可以在函数定义之前增加var that = this,然后此处修改this为that即可。
分析原因:
此处代码中的res =>{ }与普通的function(res){}函数定义功能相同,其中res即为形参对象,但前者不同的是针对this的处理上。前者是采用的是ECMAScript 6.0(关于ES 6.0与Javascript之间的关系详见:https://blog.csdn.net/u012331525/article/details/80517488)的新特性:箭头函数(关于箭头函数的详细介绍详见:https://blog.csdn.net/weixin_41593408/article/details/86771813)。
箭头函数有以下几点注意事项:
- 函数体内的
this
对象就是定义时所在的对象,而不是调用时所在的对象 - 不可以作为构造函数
- 不可以使用
arguments
对象 - 不可以使用yield命令,因此箭头函数不能用作 Generator 函数
普通函数如下代码:
var obj = {
x:1,
func: function(){ console.log(this.x) }, // 此处的 this 代表 obj
test: function(){
setTimeout(function(){
alert(this); // 因为使用了异步,在运行过程中,this发生了指针转移,不再指向obj,而是指向全局 Window对象
this.func();
},1000);
}
};
obj.test();
//报错
// TypeError: this.func is not a function
上述代码中,在test函数中定义了一个延时调用函数,其实际执行时间为1000ms之后,此时test已经执行完毕,当延时函数执行时,this值为全局window对象,而全局的window对象不存在func函数,所以报错。
箭头函数代码如下:
var obj = {
x: 1,
func: function(){ console.log(this.x) },
test: function(){
setTimeout(() => {
alert(this); // [object Object] 此处的this指向obj
this.func();
},1000);
}
};
obj.test(); // 这回this就指向obj了
同样在test中定义延时调用,但由于箭头函数特性,使其this值总是指向函数定义生效时所在对象,此处即为obj,所以不会出现上述错误。其本质要点在与实际上,箭头函数自身没有自己的this,导致其内部的this就是外层代码块的this
在微信小程序的开发工具中,已经有ES6语法转换ES5的设置,如下:
问题2:关于异步加载数据及回调函数(Callback)理解
小程序中大部分网络交互的接口实现都是异步的,因此在写小程序时,不可避免的就必须和异步进行打交道。微信小程序基本是通过Javascript+wxss+wxml(类html)组合而成,小程序中的主体逻辑是用js实现的。对于精通前端技术的人来说,javascript中的promise、callback应该是非常熟悉了,这部分我来说说自己对小程序中如何使用callback及promise实现异步处理和回调的理解及总结。我们以微信小程序中获取用户信息userInfoReadyCallback为例来说明:
app.js
App({
onLaunch: function () {
// 展示本地存储能力
var logs = wx.getStorageSync('logs') || []
logs.unshift(Date.now())
wx.setStorageSync('logs', logs)
// 登录
wx.login({
success: res => {
}
})
// 获取用户信息
wx.getSetting({
success: res => {
if (res.authSetting['scope.userInfo']) {
// 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
wx.getUserInfo({
success: res => {
// 可以将 res 发送给后台解码出 unionId
this.globalData.userInfo = res.userInfo
// 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
// 所以此处加入 callback 以防止这种情况
if (this.userInfoReadyCallback) {
this.userInfoReadyCallback(res)
}
}
})
}
}
})
},
globalData: {
userInfo: null,
}
})
index.js
/index.js
//获取应用实例
const app = getApp()
Page({
data: {
motto: '欢迎使用迷你计算器',
userInfo: {},
hasUserInfo: false,
canIUse: wx.canIUse('button.open-type.getUserInfo')
},
//事件处理函数
bindViewTap: function() {
wx.navigateTo({
url: '../logs/logs'
})
},
onLoad: function () {
if (app.globalData.userInfo) {
this.setData({
userInfo: app.globalData.userInfo,
hasUserInfo: true
})
} else if (this.data.canIUse){
// 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
// 所以此处加入 callback 以防止这种情况
app.userInfoReadyCallback = res => {
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
} else {
// 在没有 open-type=getUserInfo 版本的兼容处理
wx.getUserInfo({
success: res => {
app.globalData.userInfo = res.userInfo
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
})
}
},
getUserInfo: function(e) {
console.log(e)
app.globalData.userInfo = e.detail.userInfo
this.setData({
userInfo: e.detail.userInfo,
hasUserInfo: true
})
}
})
首先执行的是app.js的wx.getUserInfo,这个是获取用户信息的网络请求,由于其返回结果不知道在index页面加载完成之前还是之后完成,因此分为两种情况:
- 在index页面加载完成之前返回:此时优先执行app.js中success函数的代码,app.globalData.userInfo就保存了用户信息。运行到这里时,由于userInfoReadyCallback函数是在index.onload中定义的,因此此时该函数并没有被定义,所以不执行该函数。之后执行index.js中onload中的代码,执行第一个if分支,赋值给页面的userInfo和hasUserInfo
- 在index页面加载完成之后返回:此时优先执行index.js中onload中的代码,由于用户信息还没有返回,所以app.globalData.userInfo为null,执行第二个if分支,定义userInfoReadyCallback函数。随后数据被返回,执行success的代码,app.globalData.userInfo在此时才保存了用户的信息,并执行userInfoReadyCallback函数,赋值给页面的userInfo和hasUserInfo
注:因为wx.getUserInfo
是异步网络请求,不知道异步请求先执行完毕还是page.onload先执行完毕,判断的关键在于userInfoReadyCallback方法是否定义。
1. 方法(userInfoReadyCallback)如果定义了,则说明page.onload比当前方法运行的早(page已经完成初始化),app的globalData还没有数据,通过此回调可以及时的刷新数据
2. 方法如果没有定义,则说明page.onload比当前方法运行的晚(page还没有初始化),app的globalData是有值的,可以在page.onload中取globalData里面的值
因此总体来说userInfoReadyCallback
函数的作用,就是保证页面的userInfo
和hasUserInfo
被正确赋值,无论用户信息在页面加载完成之前还是之后返回。
所谓回调(callback)就是在异步调用时,当异步处理完成后要执行的代码块。
假设需求:
- 首先在数据库的medicine表中有3条药品信息记录,每条记录简单的包含药品的一些基础信息。
- 在小程序中,我们需要从medicine表中查出所有药品记录,并在页面中展示
在小程序中,微信提供的数据库访问接口都是异步的,因此不能用简单的同步模式写代码,这时就需要使用callback或者promise
我们先来看看以传统的同步模式来实现的话代码会写成如下形式:
分析如下:
按照同步模式的理解,以上代码第8行,调用this.getDbData函数查询数据库,在函数内将查询结果赋值给全局变量data中的medicine中,并输出查询结果。
第9行,在调用了getDbData函数之后,再输出变量data的内容,发现medicine变量并没有被赋值,如下图
从上边的执行结果可以看到,第9行先执行了,而getDbData中的输出后执行了,这就是因为getDbData中调用数据库的接口,在微信API中是异步实现的,因此要想实现查询获取了相关数据之后,再对数据做进一步处理就需要使用callback机制或者promise
首先,我们通过callback机制来改造如上同步调用模式为异步调用模式:
如上,在第8行增加了this.process_data作为callback传入getDbData函数中第12~14行,是callback函数的定义,并在该函数中输出相关全局变量的内容,第25行,是执行完数据库查询并且完成全局变量赋值之后,将全局变量传给callback函数并调用,整体执行结果如下:
我们可以如上文userInfoReadyCallback一样,通过判断所需数据是否为空,在回调函数中实现数据在页面中加载。
接下来,我们通过promise模式改造上述需求,代码如下:
- 如上,第15行重新定义了promise模式的函数,getDataByPromise返回的是一个promise对象
-
在第7行调用函数之后,在then中执行相关后置动作,具体执行效果如下:
完整代码如下:
Page({
data: {
"medicine": "N/A",
},
onLoad: function (options) {
// callback模式处理异步调用
// var that = this;
// this.getDbData("medicine", {}, "medicine", this.process_data)
// console.log("in onLoad:", this.data)
// promise模式处理异步调用
this.getDataByPromise("medicine", {}).then((data) => {
this.setData({
"medicine": data
})
console.log("in onLoad promise:", this.data)
})
},
getDataByPromise: function (coll_name, search_cond) {
var promise = new Promise((resolve, reject) => {
var that = this;
const db = wx.cloud.database();
db.collection(coll_name).where(search_cond).get({
success: function (res) {
console.log("in promise info:", res.data)
resolve(res.data)
},
error: function (e) {
console.log(e)
reject("查询数据库失败")
}
});
});
return promise;
},
process_data: function(data){
console.log("in onLoad callback:", this.data)
},
getDbData: function (coll_name, search_cond, data_key, cb) {
const db = wx.cloud.database()
var that = this;
var ready_data = {};
db.collection(coll_name).where(search_cond).get({
success: function (res) {
ready_data[data_key] = res.data;
that.setData(ready_data);
console.log("查询数据库完成:", that.data, res.data);
cb(that.data)
}
})
}
})
参考文档
https://blog.csdn.net/iot_player/article/details/87031698
https://blog.csdn.net/zjw_python/article/details/80641963
https://www.cnblogs.com/NKnife/p/6283605.html