一、前言

​ 简单阐述一个困扰我们已久的问题:因为异步函数中的赋值操作是不同步的(即程序不会等该函数执行完再执行下面的函数),这样就导致了在该异步函数下面的程序的赋值操作会先执行,这样就导致了赋值操作后的值为undefined。

二、 promise的概念

promise是一个对象,简单来说就是会保存一个异步操作的结果。

new Promise((resolve,reject)=>{
	//自定义的异步函数
});

看到定义的Promise有两个参数,resolvereject:

  • resolve:将异步的执行从pending(请求)变成了resolve(成功返回),是个函数执行返回。
  • reject:顾名思义“拒绝”,就是从请求变成了"失败",是个函数可以执行返回一个结果,但我们这里推荐大家返回一个错误new Error()

简答来说resolve是自定义成功的返回,reject是自定义失败的返回,下面给个例子:

new Promise((resolve,reject)=>{
	wx.request({
    	url:"localhost:8080/test",
        success(res){
            //调用成功返回回调的信息
        	resolve(res)
        },fail(err){
            //调用失败返回错误信息
        	reject(err)
        }
    })
})

三、promise的使用

一般的我们会把它封装在一个函数里,在函数里面返回promise,将上面的函数进行封装

myrequest(){
    return new Promise((resolve,reject)=>{
        wx.request({
            url:"localhost:8080/test",
            success(res){
                //调用成功返回回调的信息
                resolve(res)
            },fail(err){
                //调用失败返回错误信息
                reject(err)
            }
        })
	})
}

我们通过Promise的原型方法then拿到我们的返回值

myrequest().then((res)=>{
	console.log(res)
})

catch捕获出错的返回值

myrequest().then((res)=>{
	console.log(res)
}).catch((err)=>{
	console.log(err)
})

四、一个因为异步请求出问题的案例

下面是一个以微信小程序业务为例子的程序,这里面的大致流程和意思就是想在onLaunch函数里面调用openid然后再获取用户信息,然后经过socketopen判断是否进行websocket连接,然后进行websocket监听。

App({
globalData: {
    userInfo: null,
    openid: "",
    canIUse: wx.canIUse("button.open-type.getUserInfo"),
    host: "http://119.29.196.165:8081",
    chathost: "ws://localhost:8000/imserver/",
    SocketTask: "",
    socketOpen: false,
  },
onLaunch(){
	this.getopenid();
    this.getUserInfo();
   	this.websocketconnect(this);
    this.websocketserver();
    }
},
 // 获取用户openid
  getopenid() {
    var that = this;
    console.log("going getopenid");
      wx.cloud.callFunction({
        name: "openapi",
        success: function (res) {
          that.globalData.openid = res.result.openid;
        },
        fail: function (res) {
          console.log("失败:" + res);
        },
      });
  },
  // 获取用户信息
  getUserInfo() {
    var that = this;
    console.log("going getUserInfo");
      wx.getSetting({
        success: function (res) {
          if (res.authSetting["scope.userInfo"]) {
            // 已经授权,可以直接调用 getUserInfo 获取头像昵称
            wx.getUserInfo({
              success: function (res) {
                that.globalData.userInfo = res.userInfo;
              },
              fail(err) {
                console.log(err);
              },
            });
          }
        },
        fail(err) {
            console.log(err)
        },
      });
  },
  // 连接websocket服务器
  websocketconnect(tha) {
    var that = tha;
    // 创建Socket
      console.log(that.globalData.openid);
      that.globalData.SocketTask = wx.connectSocket({
        url: "ws://localhost:8000/imserver/" + that.globalData.openid,
        data: "data",
        header: {
          "content-type": "application/json",
        },
        method: "post",
        success: function (res) {
          that.globalData.socketOpen = true;
          console.log("WebSocket连接创建", res);
        },
        fail: function (err) {
          console.log(err);
        },
      });
  },
  // websocket监听
  websocketserver() {
    var that = this;
      that.globalData.SocketTask.onOpen((res) => {
        that.globalData.socketOpen = true;
        console.log("监听 WebSocket 连接打开事件。", res);
      });
      that.globalData.SocketTask.onClose((onClose) => {
        //如果websocket关闭了  就重新连接
        console.log("监听 WebSocket 连接关闭事件。", onClose);
        that.globalData.socketOpen = false;
        this.websocketconnect(that);
      });
      that.globalData.SocketTask.onError((onError) => {
        console.log("监听 WebSocket 错误。错误信息", onError);
        that.globalData.socketOpen = false;
      });
      that.globalData.SocketTask.onMessage((onMessage) => {
        //监听WebSocket接受到服务器的消息事件
        console.log(onMessage);
      });
  },
});

但是会发现,请求getopenid函数是异步的导致下面划红线的地方为空

image-20210228110930527

导致websocketconnect函数请求失败

五、案例的解决方案

1、运用promise解决异步转同步问题

下面运用到then式的链式写法,其本质就是一直往下传递返回一个新的Promise,也就是说then在下一步接收的是上一步返回的Promise,这样就可以是函数执行按照顺序进行了。

App({
globalData: {
    userInfo: null,
    openid: "",
    canIUse: wx.canIUse("button.open-type.getUserInfo"),
    host: "http://119.29.196.165:8081",
    chathost: "ws://localhost:8000/imserver/",
    SocketTask: "",
    socketOpen: false,
  },
onLaunch(){
	this.getopenid()
    	.then((result)=>{
    		return this.getUserInfo()
    	})
    	.then((result)=>{
        	return this.websocketconnect(this)
        ))
        .then((result)=>{
        	return this.websocketserver()
        })
        //catch是捕获错误异常,原本每一个promise都有一个catch,但是他现在有冒泡特性,所以现在只需要一个就好了
        .catch((err)=>{
       		console.log(err)
        })
    }
},
 // 获取用户openid
  getopenid() {
    var that = this;
    console.log("going getopenid");
    return new promise((reslove,reject)=>{
    	wx.cloud.callFunction({
        name: "openapi",
        success: function (res) {
          that.globalData.openid = res.result.openid;
          resolve(that.globalData.openid);
        },
        fail: function (res) {
          console.log("失败:" + res);
          reject(res);
        },
      });
    })
  },
  // 获取用户信息
  getUserInfo() {
    var that = this;
    console.log("going getUserInfo");
    return new promise((reslove,reject)=>{
    	wx.getSetting({
        success: function (res) {
          if (res.authSetting["scope.userInfo"]) {
            // 已经授权,可以直接调用 getUserInfo 获取头像昵称
            wx.getUserInfo({
              success: function (res) {
                that.globalData.userInfo = res.userInfo;
                resolve(that.globalData.userInfo);
              },
              fail(err) {
                console.log(err);
                reject(err);
              },
            });
          }
        },
        fail(err) {
          reject(err);
        },
      });
    })
  },
  // 连接websocket服务器
  websocketconnect(tha) {
    var that = tha;
    // 创建Socket
      console.log(that.globalData.openid);
      return new promise((reslove,reject)=>{
      	that.globalData.SocketTask = wx.connectSocket({
        //下面是自己在后端搭建的websocket服务器,通过不同的openid来识别并建立连接
        url: "ws://localhost:8000/imserver/" + that.globalData.openid,
        data: "data",
        header: {
          "content-type": "application/json",
        },
        method: "post",
        success: function (res) {
          that.globalData.socketOpen = true;
          console.log("WebSocket连接创建", res);
          resolve(res)
        },
        fail: function (err) {
            reject(err)
        },
      });
      })
  },
  // websocket监听
  websocketserver() {
    var that = this;
    return new promise((reslove,reject)=>{
       //下面是后端建立了websocket连接后在socket对象里面提供的监听事件
       //websocket打开监听
    	that.globalData.SocketTask.onOpen((res) => {
        that.globalData.socketOpen = true;
        console.log("监听 WebSocket 连接打开事件。", res);
      });
        //websocket关闭监听
      that.globalData.SocketTask.onClose((onClose) => {
        //如果websocket关闭了  就重新连接
        console.log("监听 WebSocket 连接关闭事件。", onClose);
        that.globalData.socketOpen = false;
        this.websocketconnect(that);
      });
        //websocket错误监听
      that.globalData.SocketTask.onError((onError) => {
        console.log("监听 WebSocket 错误。错误信息", onError);
        that.globalData.socketOpen = false;
      });
        //websocket接收到的消息的监听
      that.globalData.SocketTask.onMessage((onMessage) => {
        //监听WebSocket接受到服务器的消息事件
        console.log(onMessage);
      });
    })
  },
});

2、运用async+await+promise解决异步转同步问题

为什么有Async/Await?

我们可以使用promise来解决异步的问题,可以用then的链式表达更清晰,但是我们在发现有大量异步请求的时候,会发现充满屏幕的then,看起来比较吃力,而es7的async和await就是为了解决这种情况

什么是async/await呢?

async/await是一对好基友,缺一不可,他们的出生是为Promise服务的。可以说async/await是Promise的进化版

什么是Async

async必须声明的是一个function

async function myrequest(){

}
什么是await

await就必须是在这个async声明的函数内部使用,否则就会报错。

async function myrequest(){
	await getopenid();
}

请记住await是在等待一个Promise的异步返回,就是等待函数内部需要返回一个promise才能起到效果

async function myrequest(){
	await getopenid(){
    	return new promise((resolve,reject)=>{
        	
        })
    };
}
运用async/await解决案例
App({
  globalData: {
    userInfo: null,
    openid: "",
    canIUse: wx.canIUse("button.open-type.getUserInfo"),
    host: "http://119.29.196.165:8081",
    chathost: "ws://localhost:8000/imserver/",
    SocketTask: "",
    socketOpen: false,
  },
  onLaunch: function () {
    this.dowait();
  },
  async dowait() {
    await this.getopenid();
    await this.getUserInfo();
    await this.websocketconnect(this);
    await this.websocketserver();
  },
  //初始化云服务
  cloudinit() {
    if (!wx.cloud) {
      console.error("请使用 2.2.3 或以上的基础库以使用云能力");
    } else {
      wx.cloud.init({
        // env 参数说明:
        //   env 参数决定接下来小程序发起的云开发调用(wx.cloud.xxx)会默认请求到哪个云环境的资源
        //   此处请填入环境 ID, 环境 ID 可打开云控制台查看
        //   如不填则使用默认环境(第一个创建的环境)
        env: "school-n97ya",
        traceUser: true,
      });
    }
  },
  // 获取用户openid
  getopenid() {
    var that = this;
    console.log("going getopenid");
    return new Promise((resolve, reject) => {
      wx.cloud.callFunction({
        name: "openapi",
        success: function (res) {
          that.globalData.openid = res.result.openid;
          resolve(that.globalData.openid);
        },
        fail: function (res) {
          console.log("失败:" + res);
          reject(res);
        },
      });
    });
  },
  // 获取用户信息
  getUserInfo() {
    var that = this;
    console.log("going getUserInfo");
    return new Promise((resolve, reject) => {
      wx.getSetting({
        success: function (res) {
          if (res.authSetting["scope.userInfo"]) {
            // 已经授权,可以直接调用 getUserInfo 获取头像昵称
            wx.getUserInfo({
              success: function (res) {
                that.globalData.userInfo = res.userInfo;
                resolve(that.globalData.userInfo);
              },
              fail(err) {
                console.log(err);
                reject(err);
              },
            });
          }
        },
        fail(err) {
          reject(err);
        },
      });
    });
  },
  // 连接websocket服务器
  websocketconnect(tha) {
    var that = tha;
    // 创建Socket
    return new Promise((resolve, reject) => {
      console.log(that.globalData.openid);
      that.globalData.SocketTask = wx.connectSocket({
        url: "ws://localhost:8000/imserver/" + that.globalData.openid,
        data: "data",
        header: {
          "content-type": "application/json",
        },
        method: "post",
        success: function (res) {
          that.globalData.socketOpen = true;
          console.log("WebSocket连接创建", res);
            resolve(res)
        },
        fail: function (err) {
          console.log(err);
            reject(err)
        },
      });
    });
  },
  // websocket监听
  websocketserver() {
    var that = this;
    return new Promise((resolve, reject) => {
      that.globalData.SocketTask.onOpen((res) => {
        that.globalData.socketOpen = true;
        console.log("监听 WebSocket 连接打开事件。", res);
      });
      that.globalData.SocketTask.onClose((onClose) => {
        //如果websocket关闭了  就重新连接
        console.log("监听 WebSocket 连接关闭事件。", onClose);
        that.globalData.socketOpen = false;
        this.websocketconnect(that);
      });
      that.globalData.SocketTask.onError((onError) => {
        console.log("监听 WebSocket 错误。错误信息", onError);
        that.globalData.socketOpen = false;
      });
      that.globalData.SocketTask.onMessage((onMessage) => {
        //监听WebSocket接受到服务器的消息事件
        console.log(onMessage);
      });
    });
  },
});

Logo

基于 Vue 的企业级 UI 组件库和中后台系统解决方案,为数万开发者服务。

更多推荐