小程序同步异步:回调函数与Promise,

1、同步与异步

在小程序中网络请求wx.request不像其他框架可以设置同步或异步,wx.request在小程序中只能是异步方式。
既然是异步方式,就不能用以下的方式获取网络数据:

let data = wx.request({
    url:'',
    header:{
        appket:""
    }
})

    1
    2
    3
    4
    5
    6

而应该使用回调函数来获取请求后的数据。

wx.request({
    url:'',
    header:{
        appket:""
    },
    success:function(res){
        console.log(res)
    }
})

2、箭头函数

如果回调函数使用传统写法,即上述写法,在使用data里的数据是,this.data中的this指的并不是Page对象,所以这样无法获取data数据。通常做法是在回调函数外部用that来存储this:

let that = this;
wx.request({
    url:'',
    header:{
        appket:""
    },
    success:function(res){
        console.log(that.data.test)
    }
})

而使用箭头函数,则可以不需要that来存储this,直接使用this就行:

wx.request({
    url:'',
    header:{
        appket:""
    },
    success:(res) => {
        console.log(this.data.test)
    }
})

3、回调函数的嵌套

如果在一个函数里要调用一个异步函数,则一定要把一个回调函数作为该函数的参数。
比如一个函数getLatest()要根据index从服务器获取相应的latest对象,则应该把获取数据的操作放在回调里,然后把这个回调作为getLatest()的一个参数。
getLatest()函数定义:

  getLatest(sCallback) {
    this.request({
      url: 'classic/latest',
      success: (data) => {
        // 如果不用箭头函数,this将指代不正确
        let key = this._fullKey(data.index)
        wx.setStorageSync(key, data)
        this._setLatestIndex(data.index)
        sCallback(data)
      }
    })
  }
其中request函数也是异步函数,因此sCallback要放在request函数的回调中。
request函数定义如下:

  request(params) {
    var that = this
    var url = this.baseRestUrl + params.url;

    if (!params.method) {
      params.method = 'GET';
    }
    wx.request({
      url: url,
      data: params.data,
      method: params.method,
      header: {
        'content-type': 'application/json',
        'appkey':config.appkey
      },
      success: function (res) {
        // 判断以2(2xx)开头的状态码为正确
        // 异常不要返回到回调中,就在request中处理,记录日志并showToast一个统一的错误即可
        var code = res.statusCode.toString();
        var startChar = code.charAt(0);
        if (startChar == '2') {
          //先判断params.success是否为空,
          //如果不为空,将res.data作为参数传入params.success
          params.success && params.success(res.data);
        } else {
          params.error && params.error(res);
        }
      },
      fail: function (err) {
        params.fail && params.fail(err)
      }
    });
  }

getLatest()函数调用:

getLatest((data)=>{
  this._getLikeStatus(data.id, data.type)
  this.setData({
    classic:data
  })
})

该回调函数是用箭头函数写的。

总之,存在异步函数的嵌套时,外层函数的返回值或数据处理要放在内层函数的回调里,以此类推。

而使用Promise时就可以解决回调嵌套的问题,因为Promise保留了函数return的功能。

比如requset()是个异步函数,而getHotList()中调用了requset(),页面中又执行了getHotList()函数。因此用回调函数的写法应该有两层嵌套。下面用Promise实现:

request()函数:

 request({url,data={},method='GET'}){
        return new Promise((resolve, reject)=>{
            this._request(url,resolve,reject,data, method)
        })
    }
    _request(url,resolve, reject, data={}, method='GET'){
        wx.request({
            url:config.api_base_url + url,
            method:method,
            data:data,
            header:{
                'content-type':'application/json',
                'appkey':config.appkey
            },
            success:(res)=>{
                const code = res.statusCode.toString()
                if (code.startsWith('2')){
                    resolve(res.data)
                }
                else{
                    reject()
                    const error_code = res.data.error_code
                    this._show_error(error_code)
                }
            },
            fail:(err)=>{
                reject()
                this._show_error(1)
            }
        })
    }



getHotList()调用request()

getHotList() {
        return this.request({
            url: 'book/hot_list'
        })
    }

调用getHotList()函数:

bookModel.getHotList()
  .then(res => {
    this.setData({
      books:res
    })
  })


4、Promise与异步

实现异步的三种方式:
(1)纯粹callback;
(2)Promise;
(3)async与await(ES2017)

目前来说,由于小程序暂时不支持async与await,所以Promise是小程序处理异步的最佳解决方案。即使支持async,它也只是Promise的语法糖,所以Promise是必须要学习的基础。

Promise相对于回调函数的优势:
(1)解决了纯粹callback嵌套造成的回调地狱问题;
如果在success回调函数中再次进行异步操作,而在该异步操作的回调函数中再进行异步操作,就形成了异步嵌套,会使代码的可阅读性变得很差,造成“回调地狱”:

wx.request{
    url:'',
    header:{
        appket:""
    },
    success:(res) => {
        wx.request({
            success:(res) => {
                        wx.request({
                            success:(res) => {
                }
            }
        })
    }
})

在这里插入图片描述
当然如果只有一次回调,就没必要用Promise了。

let promise = new Promise((resolve, reject)) => {
    wx.request{
    url:'',
    header:{
        appket:""
    },
    success:(res) => {
        wx.request({
            success:(res) => {
                        wx.request({
                            success:(res) => {
                }
            }
        })
    }
 })
 promise.then((res) => {
        console.log(res)
    })
}



(2)解决了回调函数剥夺函数return能力的问题;

通常异步函数中是不能return结果的,而Promise可以解决这个问题。
在这里插入图片描述
(3)使代码更具可读性;
(4)实现多个异步等待合并;

Promise是一个对象,不是函数,对象可以保存状态,而函数不行。
5、使用promise

参考Javascript:Promise对象基础
(1)构造Promise
Promise构造器接受一个函数作为参数,这个函数有两个参数:resolve,reject,分别代表这个Promise实例成功之后的回调函数和失败之后的回调函数。
这里我们将一个异步函数getSystemInfo()作为Promise的参数。

const promise = new Promise((resolve, reject) => {
        wx.getSystemInfo({
            success: (res) => {
                resolve(res)
            },
            fail: (error) => {
                reject(error)
            }
        })
    }
})


(2)Promise 的状态
Promise有3种状态:

    Pending:进行中
    Resolved(Fulfilled):已完成
    Rejected:已失败

Promise状态的改变只有两种:

Pending --> Resolved
Pending --> Rejected
这意味着,一个Promise对象resolve之后,状态就一直停留在Resolved那里了,反过来reject也一样。
这种特点的结果是,Promise对象的状态改变之后,你再给它添加回调函数,这个函数也会执行。
这跟事件监听器很不一样 —— 你一旦错过某个事件,就没办法再捕获他了,除非这个事件再次发生。
(3).then() 和 .catch()
.then() 接收两个回调函数作为参数,第一个是当promise变成成功状态的回调函数;第二个是当promise变成失败状态的回调函数。

const promise = new Promise((resolve, reject) => {
        wx.getSystemInfo({
            success: (res) => {
                resolve(res)
            },
            fail: (error) => {
                reject(error)
            }
        })
    })
    
    promise.then( (res) => {
        console.log(res)
    },(error) => {
        console.log(error)
})

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

箭头函数简写:
在这里插入图片描述
Promise的精髓:
Promise作为对象保存了调用异步函数的结果,不需要附带任何回调函数。什么时候需要取Promise中的异步结果时,才使用.then() 和 .catch()调用一步函数。

下面针对一个获取服务器数据的request()方法分别用回调形式和Promise来写:
在这里插入图片描述
当需要在其他函数中调用该request方法时:
在这里插入图片描述
(4)promise实现链式调用
不管是then方法还是catch方法返回的都是一个新的Promise实例,这意味着Promise可以链式调用then和catch,每一个方法的返回值作为下一个方法的参数。

下面要实现多次调用API,即链式调用API,分别是错误的和正确的Promise用法:
在这里插入图片描述
嵌套式的写法又跟回调函数的写法一样了,不能体现Promise的作用。而应该把.then()写在外面,下面的.then()会接收上面的.then()的结果并作为参数继续执行。
(5)Promise.all() 和 Promise.race()

    Promise.all()

接收一个Promise对象的数组作为参数,当这个数组里的所有Promise对象全部变为resolve的时候,该方法才resolve。
如果其中一个Promise对象为reject的话,则该方法为reject。

比如有三个异步操作,都是向服务器请求数据,返回的Promise对象分别是detail ,comments ,likeStatus 。
开始获取数据前显示loading,获取完隐藏loading,这就需要使用Promise.all() 方法:

    wx.showLoading()
    const bid = options.bid
    const detail = bookModel.getDetail(bid)
    const comments = bookModel.getComments(bid)
    const likeStatus = bookModel.getLikeStatus(bid)

    Promise.all([detail, comments, likeStatus])
      .then(res => {//res是一个数组
        this.setData({
          book: res[0],
          comments: res[1].comments,
          likeStatus: res[2].like_status,
          likeCount: res[2].fav_nums
        })
        wx.hideLoading()
      })

    Promise.race()

使用方法和Promise.all一样,接收一个Promise对象数组为参数。
只要其中一个Promise对象变为Resolved或者Rejected状态,该方法返回,进行后面的处理。
https://blog.csdn.net/weixin_42195593/article/details/90550172

发表评论

电子邮件地址不会被公开。 必填项已用*标注