受欢迎的博客标签

微信小程序Promise、async、await解决异步请求问题系列之可选技术方案

Published

背景

前端需要处理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。
在onLoad的时候调用,但我无论怎么写,getWeather()函数都是执行在前,这是为什么呢?

return 一个 Promise 然后链式调用。

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

https://ninghao.net/blog/5508