首頁  >  文章  >  web前端  >  Promise實務 實作微信小程式介面封裝

Promise實務 實作微信小程式介面封裝

hzc
hzc轉載
2020-06-15 10:55:122541瀏覽

相信很多開發者都曾經遇到過回檔地獄的問題。由於微信小程式的API基本上都是基於回呼函數的非同步操作,如果不使用其他框架或封裝API,特別是使用較多的wx.request(),基本上很快就會遇到回呼地獄的問題,維護起來十分痛苦。

舉個例子

假設此時在正在開發一個社交小程序,其中有一個功能的是,小程序用戶在登入後,可以查看附近的人。

假設使用以下的實作思路,我們透過wx.getLocation()取得使用者目前位置,然後透過wx.request()請求後端資料。但在此之前需要登錄,參考先前官方文件推薦的登入方式,先呼叫wx.login()取得code,再用wx.request()請求開發者伺服器,成功回傳自訂登入態(一般為access_token或其他令牌形式),之後再用自訂登入態請求業務資料。

為了方便看,我把官方文件裡的登入流程貼出來⬇️

Promise實務 實作微信小程式介面封裝

思路確定後,開始嘗試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中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除