小程序学习:回调函数与Promise

本文详细介绍了小程序中处理异步请求的方法,从同步与异步的概念,箭头函数的使用,回调函数的嵌套,到Promise解决回调地狱问题及其优势。通过示例展示了Promise的构造、状态变化、.then()和.catch()方法的使用,以及如何实现链式调用和并行处理多个Promise。最后,讨论了Promise.all()和Promise.race()在处理并发请求中的应用。

1、同步与异步

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

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

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

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)
})

箭头函数简写:
在这里插入图片描述
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状态,该方法返回,进行后面的处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值