Maintain your request queue and handle token exceptions

Keywords: Javascript network

Preface

Network request is the most basic and core requirement in development. It is very important to encapsulate a stable and high availability request. In addition to the input parameters, the encapsulated content is more about exception handling in the request. This article shares my practice in dealing with token exceptions. By maintaining the request queue, I can resend the request and reduce the repeated request of token.

Public request method

The following is an example of encapsulating wechat applet request, which is a basic public request:

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) { // fail
          reject(res.data)
        }
        if (res.data.code == 1) { // Success
          resolve(res.data)
        }
        if (res.data.code == -1) { // token expired
          // token expiration processing
        }
      },
      fail: reject
    })
  })
}

token expired resend request

The internal getToken method stores the token locally

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)
  }
}

When the token expires, after waiting for getToken, send the request again to resolve the result

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
    })
  })
}

It seems that there is no problem in this way, but since there is no internal restriction on processing, n requests will initiate n getToken requests. Of course, this is not what we want. We repeatedly launched wxLogin twice as follows:

Maintenance request queue

Ideally, after the token expires, a getToken request is initiated. Every time a request comes in, put it in the queue, wait for the getToken to complete, and execute all the requests in the queue.

In this way, we need to define the request queue qeueu and the token request ID isTokening, as well as the join queue method pushQeueu and the execute queue method 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)
      // Clear queue after task execution
      if(index === this.qeueu.length-1){
        this.qeueu.length = 0
      }
    })
  }
}

The treatment is as follows:

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
    })
  })
}

After the getToken request is initiated, setting isTokening to true indicates that the request is in progress. When another request enters, the getToken will not be sent repeatedly.

Handling getToken errors

getToken when an error occurs, we should catch the error, do not continue to execute the request queue and empty the queue

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()
    }
  }
}

Written in the end

The above is what I am doing to handle token exceptions. If you have better practices or suggestions, please contact me~

Posted by ron814 on Sun, 05 Apr 2020 10:45:59 -0700