最近开发一个便签小程序的时候,有这样一个需求:用户可以在写便签的时候添加一个或多个图片。
对于这个需求,我们用户按下保存键时,内部具体的实现上是这样的逻辑:
首先检测用户是否传入了图片,如果存储本地图片地址的数组长度>=1,则将图片数组放入上传图片的函数。
由于小程序网络请求大小限制,我们只能采取循环上传单文件,然后收集每次请求的结果--图片在服务器的地址,最后将结果放在一个数组中供后续的操作使用。
当图片上传函数全部执行完毕后,将数组中的图片数组取出来,赋值到日记对象中,再将整个日记对象提交到服务器。
服务器返回保存成功或失败。
思路其实非常清晰简单,但是在代码实现上却翻了大跟头。
异步带来的问题
小程序的网络请求是异步的:我们无法通过return来将网络请求结果返回出来使用。
wx.request({ //...省略其他属性 success: function (res) { }, fail: function (res) { } })
例如在微信中发送网络请求,我们只能使用微信提供的方法wx.xxx,其中请求的结果保存在res中,而res无法直接return得到。
解决:res虽然无法直接获取,但是我们能通过将需要使用到这个请求结果的业务逻辑代码放入这个网络请求的回调函数中直接读取网络请求结果,也就是一切都需要通过回调来解决。
wx.request({ //...省略其他属性 success: function (res) { console.log(res); //接业务逻辑代码 }, fail: function (res) { console.log(res); } })
例如这个微信的网络请求,我们可以通过success和fail的回调函数来读取res的值从而完成依赖res结果的业务逻辑。
回调地狱
虽然解决了结果获取的问题,但是又产生了另一个问题,当多个请求中有明确的先后顺序时,回调会嵌套的很厉害,造成回调地狱,代码可读性和可维护性都会很差。
例如对于一个日记页面,需要先请求到页面的数据(里面包含了图片数据和其他数据的地址),再根据页面数据去请求图片数据后再请求音频数据。例如以下代码:
//请求页面整体数据 wx.request({ //...省略其他属性 success: function (res) {//成功 //请求图片数据 wx.request({ success: function (res) {//成功 //请求音频数据 wx.request({ success: function (res) {//成功 }, fail: function (res) {//失败 console.log("请求失败:"+res); } }) }, fail: function (res) {//失败 console.log("请求失败:"+res); } }) }, fail: function (res) {//失败 console.log("请求失败:"+res); } })
如何优化?幸运的是,在es6里面我们可以用promise去优化我们的回调,用then代替回调,首先将网络请求封装成一个Promise:
// 后台post请求 function postRequest(posturl, postdata) { return new Promise((resolve, reject) => { wx.request({ //省略其他属性 success: function (res) { console.log("at post request: 请求成功") resolve(res.data)//设置promise成功标志 }, fail: function (res) { console.log("at post request: 请求失败") reject(res.data)//设置promise失败标志 } }) }); }
这样封装以后,我们的网络请求会在success和fail后回调resolve,这样可以告诉promise,“hey,我完成我的工作了,你可以进行你的then操作了”,这样就可以用then来简化嵌套逻辑。使用promise来完成上面那个问题的请求将会是这样的:
postRequest(posturl,postdata) .then(function(res){ //业务逻辑 //调用下一个请求 return postRequest(next_posturl,next_postdata); }) .then(function(res){ //业务逻辑 //调用下一个请求 return postRequest(next_next_posturl,next_next_postdata); }) .then(function(res){ //业务逻辑 });
是不是简洁的多~
一个看似简单的需求
我们的有一个很简单的需求是需要对一组数量不定的图片做分别上传(因为微信限制所以无法做多上传),并且在上传完成以后需要获取到所有的返回结果。
那么用我们前面的回调函数+then的话,很自然的想到这样的写法
postRequest(posturl,postdata) .then(function(res){ //获取返回res //上传下一个图片 return postRequest(next_posturl,next_postdata); }) .then(function(res){ //获取返回res //上传下一个图片 return postRequest(next_next_posturl,next_next_postdata); }) .then(function(res){ //获取返回res });
这样看起来很简单明了,但是我的图片数量是不定的,怎么动态的构建.then.then.then这样的链式调用呢?经过我的研究后发现可以通过一个辅助的promise链去完成主链的链式构建。