背景
前端需要处理B需要拿到A请求后的参数,C需要拿到B请求后的参数的接口。
方案
可选的技术方案有3种:
1.ES5的多层嵌套
2.ES6的Promise多层then
3.ES7的async-await
1.微信小程序异步请求问题
案列1:
微信小程序中自定义两个函数,一个是获取位置,一个是获取天气,
在Page中:
Page({
getLocalCity: function(){},
getWeather: function(){},
})
调用时这样的:
Page({
onLoad:function(){
this.getLocalCity();
this.getWeather();
}
})
getWeather用了wx.request() API,getLocalCity是用wx.getLoaction(),紧接着用高德的小程序SDK。 return 一个 Promise 然后链式调用。
在onLoad的时候调用,但我无论怎么写,getWeather()函数都是执行在前,这是为什么呢?getLocalCity()
{
return new Promise(resolve => {
wx.request({ url: "", success: res =>
{ // ... return resolve(); }, }) });
},
getWeather(){ // ... },
onLoad()
{
this.getLocalCity()
.then(
result => { this.getWeather(); });
}
案列2:微信小程序为了提高用户体验,提供的api大部分都是异步操作,除了数据缓存操作里面有一些同步操作。例如我要写一个公共方法,发起网络请求,去后台去一些数据,成功之后,再做一些操作,但是由于wx.request是异步请求,就会
导致,网络请求还没结束,就会执行后面的代码,从而引起异常,怎么解决这种异步导致的问题呢,当然是promise了。
案列3:连续调用三个云函数,会按着这样的顺序依次执行吗?如果想要返回的数值按顺序依次返回应该怎么办?
三个都是异步的
函数执行是顺序的,返回值的时间不是固定的。
要同步就要用promise或者ansys await
2.同步 API
2.1 官方约定,以 Sync 结尾的 API 都是同步 API, 如 wx.setStorageSync,wx.getSystemInfoSync 等;
2.2 某些API尽管不是以Sync结尾,但是仍然属于同步API,如wx.createWorker,wx.getBackgroundAudioManager
小程序按顺序执行的三种方式
1.ES5的回调函数执行,后一个方法写到前一个的回调函数中从而实现顺序执行,缺点是嵌套太多,代码混乱
call
asyncFunc1(function(){
//...
asyncFunc2(function(){
//...
asyncFunc3(function(){
//...
asyncFunc4(function(){
//...
asyncFunc5(function(){
//...
});
});
});
});
});
example
有一个过程需要依次干这些事情:
wx.getStorage() 获取缓存数据,检查登录状态
wx.getSetting() 获取配置信息,
wx.login() 使用配置信息进行登录
wx.getUserInfo() 登录后获取用户信息
wx.request() 向业务服务器发起数据请求
回调函数代码:
wx.getStorage({
fail: () => {
wx.getSetting({
success: settings => {
wx.login({
success: ({ code }) => {
wx.getUesrInfo({
code,
success: (userInfo) => {
wx.request({
success: () => {
// do something
}
});
}
});
}
});
}
});
}
});
2.用ES6的promise对函数封装以便实现链式调用
function
function asyncFunc1(){
return new Promise(function (resolve, reject) {
//...
})
}
// asyncFunc2,3,4,5也实现成跟asyncFunc1一样的方式...
call
asyncFunc1()
.then(asyncFunc2)
.then(asyncFunc3)
.then(asyncFunc4)
.then(asyncFunc5);
这些异步函数就会按照顺序一个一个依次执行。
3.ES7的async-await 同步执行,此方法等待前面方法执行完毕才继续后续执行。代码可读性好。
微信小程序中使用Async-await方法异步请求变为同步请求
https://www.cnblogs.com/cckui/p/10231801.html
1.wx.cloud.uploadFile 是异步请求
可能出项的情况:在wx.cloud.uploadFile还未执行完就执行了page的onLoad;
解决办法:使用Promise变为同步请求
2.小程序原生发送异步请求: wx.request
// 原生发送异步请求
wx.request({
url: '', // 请求的路径
method: "", // 请求的方式
data: {}, // 请求的数据
header: {}, // 请求头
success: (res) => {
// res 响应的数据
}
})
promise
// 1.创建对象
const p = new Promise( (resolve,reject) => {
// 逻辑代码
if(){
resolve(data)
}else{
reject(err)
}
})
// 2.调用方法
p.then(res=>{
console.log(res)
}).catch(err=>{
console.log(err)
})
封装
step 1:原型
//getNewObjectId
getNewObjectIdSync: function () {
var that = this;
return new Promise((resolve, reject) => {
// 逻辑:发送请求到服务器
wx.request({
//业务代码
success: res => {
resolve(res);
},
fail: err => {
reject(err);
}
});
});
},
step 2:发起请求
//getNewObjectId
getNewObjectIdSync: function () {
var that = this;
return new Promise((resolve, reject) => {
// 逻辑:发送请求到服务器
wx.request({
//业务代码
url: 'https://api.usdotnet.com/api/ObjectId', //仅为示例,并非真实的接口地址
data: {
},
header: {
'content-type': 'application/json' // 默认值
},
//---
success: res => {
resolve(res);
},
fail: err => {
reject(err);
}
});
});
},
step 3:获取结果
//getNewObjectId
getNewObjectIdSync: function () {
var that = this;
return new Promise((resolve, reject) => {
// 逻辑:发送请求到服务器
wx.request({
//step 1:-添加发起请求业务代码
url: 'https://api.usdotnet.com/api/ObjectId', //仅为示例,并非真实的接口地址
data: {
},
header: {
'content-type': 'application/json' // 默认值
},
//---end step 1-----
success: res => {
//step 2:-添加发起请求业务代码--
let result = res.data.data;
console.log('调用云函数 getNewObjectIdSync() res:', res)
//---end step 2-----
resolve(res);
},
fail: err => {
reject(err);
}
});
});
},
wx.request 请求的封装
------------------- 第一, 在utils下新建一个js文件,进行封装 -----------------------
// promise 特点:一创建就立即执行,一般情况下解决这个问题我们会将其封装为一个函数
// options:请求时的参数对象
function myrequest(options) {
return new Promise((resolve, reject) => {
// 逻辑:发送请求到服务器
wx.request({
url: options.url,
method: options.method || "GET",
data: options.data || {},
header: options.header || {},
success: res => {
resolve(res);
},
fail: err => {
reject(err);
}
});
});
}
// 暴露给外界
export default myrequest;
封装为Promise过程,红色为在原有代码基础上添加的代码,绿色部分为原来的代码
getEmployeeByOpenIdFromApi2: function (openid) {
return new Promise((resolve, reject) => {
wx.request({
url: 'https://api.demo.com/api/Employee/EmployeesByOpenid/' + openid, //仅为示例,并非真实的接口地址
data: {
},
header: {
'content-type': 'application/json' // 默认值
},
success(res) {
console.log(res.data)
console.log("app.js开始调用https://api.demo.com/api/Employee/EmployeesByOpenid/" + openid)
https://api.demo.com/api/Employee/EmployeesByOpenid/
//json对象转成json字符串
var jsonobj = res.data;
var jsonstr = JSON.stringify(jsonobj);
console.log("app.js获取的员工信息为:jsonstr==" + jsonstr)
if (res.data.length > 0) {
getApp().globalData.Employee = res.data[0];
// this.app.globalData.Employee = res.data[0];
}
resolve(res);
},
fail: function (res) {
console.log("失败" + res);
reject(err);
}
})
});
},
page.js onLoad()改为异步方式,红色部分删除
onLoad: function (options) {
async onLoad(options) {
async onLoad(options) {
//从Web api 服务器读取员工数据,并填入globalData
await getApp().getEmployeeByOpenIdFromApi2(getApp().globalData.openid);
this.setData({
Employee: getApp().globalData.Employee,
})
2.Promise.all和Promise.race
2.1 Promise.all(iterable)
Promise.all(iterable) 方法指当所有在可迭代参数中的 promises 已完成,或者第一个传递的 promise(指 reject)失败时,返回 promise。iterable为可迭代对象,但是一般为数组。返回值也是一个Promise对象。
需要明确的几点,Promise.all是并发执行的同时运行多个Promise对象,而且返回的Promise对象的参数是一个数组,数组中的各项也是可迭代对象执行的顺序返回。
Promise.race(iterable) 方法返回一个新的 promise,参数iterable中只要有一个promise对象”完成(resolve)”或”失败(reject)”,新的promise就会立刻”完成(resolve)”或者”失败(reject)”,并获得之前那个promise对象的返回值或者错误原因。所以只要iterable中有一个完成或者失败就立即返回一个promise对象。
2.2 Promise.race
根据race这个单词为赛跑也能得出,最先到达的立即返回一个promise对象。
Promise 使用方法
// index.js
exports.main = async (event, context) => {
return new Promise((resolve, reject) => {
// 在 3 秒后返回结果给调用方(小程序 / 其他云函数)
setTimeout(() => {
resolve(event.a + event.b)
}, 3000)
})
}
假设云函数名字为 test,开启本地调试/上传部署该云函数后,我们可以在小程序端测试调用:
// 在小程序代码中:
wx.cloud.callFunction({
name: 'test',
data: {
a: 1,
b: 2,
},
complete: res => {
console.log('callFunction test result: ', res)
},
})
此时应该看到调试器输出:
callFunction test result: 3
step 2:用 Promise 的形式定义函数
async/await是和Promise配合使用的。所以要用用 Promise 的形式来定义函数
wx.request
postData: function(){
return new Promise((resolve, reject) => {
wx.request({
url: 'https://tencentcloud.cdhwdl.com:3000',
method: 'POST',
data: {
x:""
},
success (res) {
console.log(res)
resolve(res.data)
},
fail (err) {
console.log(err)
reject(err)
}
})
})
},
call
// 初始化
async init () {
await this.postData()
},
const authorize = (setting) => () => {
return new Promise((resolve, reject) => {
wx.getSetting({
success: (response) => {
if (response.authSetting[setting]) {
resolve(true)
}
wx.authorize({
scope: setting,
success: () => {
resolve(true)
},
fail: () => {
reject(false)
}
})
}
})
})
}
云函数这样封装
//从数据库中获取管理后台密码 同步方式
async getAdminPassword() {
const db = wx.cloud.database();
await db.collection(configCollection.Setting).where({
Name: "AdminPassword"
}).get({
success: res => {
this.setData({
AdminPassword: res.data[0], //返回的是一个数组,取第一个,
})
},
fail: err => {
console.log(err)
}
})
},
step 3:用函数创建函数
const authUserInfo = authorize('scope.userInfo')
step 4:call
onLoad: function (options) {
this.init ();
},
async onTapSubmitButton () {
try {
await authUserInfo()
console.log('yes')
} catch (error) {
console.log('no')
}
}
async init () {
try {
await this.getAdminPassword();
console.log('run await getAdminPassword() ok')
} catch (error) {
console.log('run await getAdminPassword() ok')
}
},
come from:https://github.com/zarknight/owx/blob/master/pages/index/index.js
async onLoad(options) {
$init(this)
try {
const session = Session.get()
if (session) {
this.data.logged = true;
this.data.userInfo = session.userInfo;
const res = await $request({ url: config.url.profile })
console.log(">>>2>>> remote user:", res)
$digest(this)
}
} catch (err) {
console.log("+++2+++ error:", err)
}
}
https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/functions/async.htm