維護你的請求佇列,處理token異常

WahFung發表於2020-04-04

前言

網路請求是開發中最基礎也是最核心的需求,封裝一個穩定且可用性高的請求也顯得尤為重要。通常封裝的內容除了入參之外,更多的是請求中的異常處理。本文分享下我在處理 token 異常方面的做法,通過維護請求佇列,實現重發請求,減少 token 重複請求。

公共請求方法

下面以封裝微信小程式請求作為例子,這是一個基礎的公共請求:

common({ baseUrl = this.baseUrl, method, url, data, header }) {
  return new Promise((resolve, reject) => {
    let token = wx.$utils.getStorageToken()
    wx.request({
      method,
      url: baseUrl + url,
      data,
      header: {
        'Content-Type': 'application/x-www-form-urlencoded',
        token,
        ...header
      },
      success: (res) => {
        if (res.data.code == 0 || res.data.code == 500) { // 失敗
          reject(res.data)
        }
        if (res.data.code == 1) { // 成功
          resolve(res.data)
        }
        if (res.data.code == -1) { // token過期
          // token過期處理
        }
      },
      fail: reject
    })
  })
}
複製程式碼

token過期重發請求

getToken 方法內部會將 token 儲存到本地中

success: (res) => {
  res = res.data
  if (res.code == 0) {
    reject(res.msg)
  }
  if (res.code == 1) {
    wx.setStorageSync('loginInfo', res.data)
    resolve(res.data.token)
  }
}
複製程式碼

token 過期,在等待 getToken 後,再次傳送請求,將結果 resolve

common({ baseUrl = this.baseUrl, method, url, data, header }) {
  return new Promise((resolve, reject) => {
    let token = wx.$utils.getStorageToken()
    wx.request({
      method,
      url: baseUrl + url,
      data,
      header: {
        'Content-Type': 'application/x-www-form-urlencoded',
        token,
        ...header
      },
      success: async (res) => {
        if (res.data.code == 0 || res.data.code == 500) {
          reject(res.data)
        }
        if (res.data.code == 1) {
          resolve(res.data)
        }
        if (res.data.code == -1) {
+         await this.getToken()
+         this.common({ baseUrl, method, url, data, header })
+           .then(resolve)
+           .catch(reject)
        }
      },
      fail: reject
    })
  })
}
複製程式碼

這樣看起來好像沒什麼問題,但由於內部沒有限制處理,有 n 個請求就會發起 n 個 getToken 請求。這當然不是我們想要的,就像下面這樣重複發起了兩次 wxLogin

維護你的請求佇列,處理token異常

維護請求佇列

理想的情況是:token 過期後,發起一個 getToken 請求。每當有請求進來,將它存入佇列中,等待 getToken 完成,執行佇列中的所有請求。

這樣我們需要定義請求佇列 qeueutoken 請求的標識 isTokening,還有加入佇列方法 pushQeueu 和執行佇列方法 execQeueu

{
  qeueu: [],
  isTokening: false,
  pushQeueu({ method, url, data, header, resolve, reject }){
    this.qeueu.push({
      data: {
        method, url, data, header
      },
      resolve,
      reject,
      request: (data)=> this.common(data)
    })
  },
  execQeueu(){
    this.qeueu.forEach((item, index) => {
      item.request(item.data)
        .then(item.resolve)
        .catch(item.reject)
      // 執行完任務後 清空佇列
      if(index === this.qeueu.length-1){
        this.qeueu.length = 0
      }
    })
  }
}
複製程式碼

處理如下:

common({ baseUrl = this.baseUrl, method, url, data, header }) {
  return new Promise((resolve, reject) => {
    let token = wx.$utils.getStorageToken()
    wx.request({
      method,
      url: baseUrl + url,
      data,
      header: {
        'Content-Type': 'application/x-www-form-urlencoded',
        token,
        ...header
      },
      success: async (res) => {
        if (res.data.code == 0 || res.data.code == 500) {
          reject(res.data)
        }
        if (res.data.code == 1) {
          resolve(res.data)
        }
        if (res.data.code == -1) {
+         this.pushQeueu({ method, url, data, header, resolve, reject })
+         if(this.isTokening === false){
+           this.isTokening = true
+           await this.getToken()
+           this.isTokening = false
+           this.execQeueu()
+         }
        }
      },
      fail: reject
    })
  })
}
複製程式碼

發起 getToken 請求後,將 isTokening 置為 true 表示正在請求中。當再有請求進入時,則不會再重複傳送 getToken

處理getToken錯誤

getToken 在發生錯誤時,我們應當捕獲錯誤,不繼續執行請求佇列並清空佇列

if (res.data.code == -1) {
  this.pushQeueu({ method, url, data, header, resolve })
  if(this.isTokening === false){
    this.isTokening = true
    let err = await this.getToken().then(res => null).catch(err => err)
    if(err){
      this.qeueu.length = 0
      console.error(err)
    }else{
      this.isTokening = false
      this.execQeueu()
    }
  }
}
複製程式碼

寫在最後

以上是我在處理 token 異常的做法,如果你有更好的做法或建議,歡迎交流~

相關文章