• 技术文章 >web前端 >js教程

    Promise实践 实现微信小程序接口封装

    hzchzc2020-06-15 10:55:12转载657
    相信很多开发者都遇到过回调地狱的问题。由于微信小程序的API基本都是基于回调函数的异步操作,如果不使用其他框架或者封装API,特别是使用较多的wx.request(),基本很快就会遇到回调地狱的问题,维护起来十分痛苦。

    举个例子

    假设此时在正在开发一个社交小程序,其中有一个功能的是,小程序用户在登录后,可以查看附近的人。

    假设使用以下的实现思路,我们通过wx.getLocation()获取用户当前位置,然后通过wx.request()请求后端数据。但在此之前需要登录,参考之前官方文档推荐的登录方式,先调用wx.login()获取code,再用wx.request()请求开发者服务器,成功返回自定义登录态(一般为access_token或其他令牌形式),之后再用自定义登录态请求业务数据。

    为了方便看,我把官方文档里的登录流程贴出来⬇️

    28750a8cc0cf3bb67705f8fb097fa62.png

    思路确定后,开始尝试coding(以下代码不建议看完)

    /* 以下为Page对象的方法 */
    
    getNearby: function() {
      // 判断是否已认证,可采用wx.checkSession()方案
      if (isAuth) {
      // TODO: 获取业务数据
        return
      }
      
      // wx.login获取code
      wx.login({
        success(res) {
          if (res.code) {
          
          // 获取自定义登录态
          wx.request({
            url,
            method, 
            headers,
            data,
            success(res) {
              // 请求成功
              if (res.statuCode === 200) {
                // 读取响应体中的自定义登录态
                let token = res.data.token
                // 保存自定义登录态
                wx.setStorageSync("assess_token", token)
                
                // 获取位置信息
                wx.getLocation({
                  success(res) {
                    let { latitude, longitude } = res
                    
                    // 请求业务数据
                    wx.request({
                      url, 
                      method, 
                      header,
                      data: { latitude, longitude },
                      success(res) {
                        // 请求成功
                        if (res.statuCode === 200) {
                          let data = res.data
                          // 数据渲染到V层
                          this.setData({ list: data })
                        }
                        // 请求失败
                        else if (res.statuCode === 400) {
                          // TODO
                        }
                        // 其他错误情况状态码处理
                        // TODO
                      }, 
                      fail(err) {
                        // 调用失败处理
                      }
                    })
                    
                  },
                  fail(err) {
                    // 调用失败处理
                  }
                })
              }
              // 请求失败
              else if (res.statuCode == 400) {
                // TODO
              }
              // 其他错误情况的状态码处理
              },
              fail(err) {
                // 调用失败处理
              }
            })
          } 
          else {
            // TODO
            // 登录失败
          }
        }, 
        fail(err) {
          // wx.login()调用失败处理
          // TODO: ...
        }
      }) 
    }

    回调地狱出现了。气功波代码,别说别人,就连自己看都会觉得恶心。

    某天英明的产品经理站了出来,说我们可以加点XXXXX,你可能还得找个地方嵌套其他微信接口或者多加几个if else分支,到时候就找个地方哭吧。

    解决方案

    从某种意义上来说,当今风暴式的前端生态,仰仗于Node以及ES6+的出现。

    ES6后对于异步有多种解决方案。一种是采用generator/yield,但generator函数使用起来其实比较麻烦。另外一种是采用Promise,相对比较简单。ES7也可以采用async/await,但本质上async/await也是基于Promise。下面介绍Promise

    Promise


    Promise创建

    创建Promise很简单,Promise本身是一个构造函数。通过new创建。构造函数的参数为一个回调函数,回调函数有两个参数为resolve和reject(无需手动维护)。resolve和reject是用来改变状态。关于状态放到后边讲。

    // Promise实例的创建
    let p = new Promise((resolve, reject) => {
      // TODO
    })

    Promise有个缺点,一旦创建便会立刻执行。所以一般会用一个函数进行包装。

    let getPromise = () => {
      return new Promise((resolve, reject) => {
        // TODO    
      })
    }

    Promise状态

    Promise实例有三种状态,pendingresolvedrejectedPromise实例创建后就会处于pending状态。回调函数中的resolvereject就是用来改变Promise实例状态的。当调用resolve时,Promise实例会从pending变成resolved状态,表示成功。当调用reject时,Promise实例会从pending变成rejected状态,表示失败。

    let getPromise = () => {
      return new Promise((resolve, reject) => {
        // TODO  
        // 处理结果
        if (result) {
          resolve(successObject)
        } 
        else {
          reject(error)
        }
      })
    }

    常用方法

    最常用的方法为then()和catch()这两个方法,通过then()的传递效用就可以解决回调地狱的问题。

    其中then()可接收两个参数,都是回调函数,第一个回调函数用来处理resolved状态,参数为Promise实例调用resolve传递的成功对象。第二回调函数用来处理rejected状态,参数为调用Promise实例调用reject传递的错误对象。

    实际中then()我们一般只用来处理resolved的情况,即只传递第一个回调函数。对于rejected情况更多是采用catch()统一处理。

    let getPromise = () => {
      return new Promise((resolve, reject) => {
        // TODO  
        // 处理结果
        if (result) {
          resolve(successObject)
        } 
        else {
          reject(error)
        }
      })
    }
    
    getPromise()
      .then(res => {
        console.log(res)
        // TODO
      })
      .catch(err => {
        //TODO
      })

    使用then()方法可以继续返回一个Promise对象,通过return一个新的Promise,可以持续的向下传递。

    getPromise()
      .then(res => {	//第一层Promise
        console.log(res)
        // TODO
        return getPromise()
        )
      .then(res => {	// 第二层Promise
        console.log(res)
        // TODO
      })
      .catch(err => {
        // TODO
      })

    其他常用方法有诸如Promise.all()Promise.race()。当需要等待多个Promise结果时会采用。两个方法都是接收一个由Promise组成的对象数组。使用Promise.all()时,只有当全部的Promise对象全部resolved Promise.all()状态才是resolved。而Promise.race()只需有一个Promise对象为resolved时,其状态就为resolved。

    更多方法可阅读相关文档。

    封装小程序接口


    学习了Promise基础,通过封装异步操作,使用Promise链就可以解决回调地狱问题。

    因为wx.request()使用频率比较高,先对wx.request()封装。

    /* 可以将公用的方法挂在app.js中 */
    
    request: function(method, url, header, data) {
      return new Promise((resolve, reject) => {
        wx.request({
          method, 
          url, 
          header, 
          data,
          success(res) {
            resolve(res)
          },
          fail(err) {
            reject(err)
          }
        })
      })
    }

    基本框架就这样,我们可以进一步修改,比如请求url的基础路径,添加一些公用的header,针对状态码做一些全局处理等。

    request: function(method, url, header = {}, data = {}) {
      // 启动时可将storage中的令牌挂到app.js 
      let token = app.assess_token
      if (token) {
        header["Authorization"] = token
      }
      return new Promise((resolve, reject) => {
        wx.request({
          method, 
          url: "https://api.domain.com/v1" + url,
          header, 
          data,
          success(res) {
            // 请求成功
            if (res.statusCode === 200) {
              resolve(res)
            }
            // 请求成功无响应体
            else if (res.statusCode === 204) {
              /* 
              可做一些成功提示,
              如调用wx.showToast()、wx.showModal()或自定义弹出层等 
              */
              resolve(res)
            }
            // 未认证
            else if (res.statusCode === 401) {
              /* 可做一些错误提示,或者直接跳转至登录页面等 */
              reject(res)
            }
            else if (res.statusCode == 400) {
            /* 可做一些错误提示*/
              reject(res)
            }
            else if (res.statuCode === 403) {
              /* 无权限错误提示*/
              reject(res)
            }
            // ...其他状态码处理
          },
          fail(err) {
            /* 可做一些全局错误提示,如网络错误等 */
            reject(err)
          }
        })
      })
    }

    封装之后,举个例子,发送请求就可以修改为

    /* 方法体中 */
    let app = getApp()
    
    app.request("POST", "/auth", {}, { username, password })	 
      .then(res => {  // 第一层请求
        // TODO 成功处理
        return app.request("GET", "/goods", {}, {})
      })
      .then(res => {	// 第二层请求
        // TODO 成功处理
        // 渲染视图
      })
      .catch(err => {
        // TODO 错误处理
      })

    封装一下其他的微信接口

    /* 可以将公用的方法挂在app.js中 */
    wxLogin: function() {
      return new Promise((resovle, reject) => {
        wx.login({
          success(res) {
            if (res.code) {
              resovle(res)
            }
            else {
              reject({ message: "登录失败" })
            }
          },
          fail(err) {
            reject(err)
          }
        })
      })
    }
    
    getLocation: function() {
      return new Promise((resolve, reject) => {
        wx.getLocation({
          success(res) {
            resolve(res)
          },
          fail(err) {
            reject(err)
          }
        })
      })
    }

    对于最初的例子,可以就修改为

    /* Page对象的方法 */
    
    getNearby: function() { 
      // 判断是否已认证,可采用wx.checkSession()方案
      if (isAuth) {
        // TODO: 获取业务数据
        return
      }
      
      app.wxLogin()
        .then(res => {
          // 将code发送给开发者服务器,获取自定义登录态
          return app.request("POST", "/auth", {}, { code, res.code })
        })
        .then(res => {
          // 保存自定义登录态
          setStorage("access_token", res.data.access_token)
          // TODO: 其他登录成功操作...	
          return app.getLocation()
        })
        .then(({ latitude, longitude }) => {
          let url = "/nearby?latitude=" + latitude + "&longitude=" + longitude
          return app.request("GET", url)
        })
        .then(res => {
          // TODO: 数据处理
          let data = res.data
          // 渲染视图层
          this.setData({ data })
        })
        .catch(err => {
          // TODO 错误处理
        })
        
    }

    之后若有需添加新的请求或者其他异步操作,直接在Promise链上操作就行了。

    推荐教程:《微信小程序

    以上就是Promise实践 实现微信小程序接口封装的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:掘金社区,如有侵犯,请联系admin@php.cn删除
    专题推荐:Promise
    上一篇:前端 Promise 常见的一些应用场景 下一篇:javascript中的split方法详解
    线上培训班

    相关文章推荐

    • 如何使用JavaScript中的promise对象• es6-promise源码的分析• 实现Promise的详细步骤• promise是什么意思

    全部评论我要评论

  • 取消发布评论发送
  • 1/1

    PHP中文网