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

前言

網路請求是開發中最基礎也是最核心的需求,封裝一個穩定且可用性高的請求也顯得尤為重要。通常封裝的內容除了入參之外,更多的是請求中的異常處理。本文分享下我在處理 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 過期後,發起一個 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 異常的做法,如果你有更好的做法或建議,歡迎交流~