微信小程序request类优化

Posted by Eric on November 23, 2018

由于微信小程序登录态也就是token会失效,所以在请求后台接口的时候会返回401,需要重新调用登录接口获取刷新token才能继续进行。碰到未授权的情况可以有以下几种处理方案:

  1. 直接跳转会登录页,让用户重新登录。
  2. 在页面显示重试按钮,让用户点击重试。
  3. 接口返回401后直接调用登录接口刷新token后再重新请求直接的接口。

在用户体验方面第三种的方式固然是最好的,然后碰到的坑也多……

直接上代码

...
	if (res.statusCode === 401) {
      console.log(401)

      if (res.requestObj.reTryCount === 3) {
        console.log('401重试3次')
        wx.showToast({
          title: '系统异常,请稍后再试',
          icon: 'none',
          image: '',
          duration: 2000,
          mask: true,
        })

        throw res
      }

      Promise.resolve()
        .then(() => login())
        .catch(error => {
       	  console.log(error)
        })

      const reRequest = () => {

        const _count = reTryCount + 1
        return request(
          ...res.requestObj,
          _count)
      }

      return LOGIN_INSTANCE ? LOGIN_INSTANCE.then(()=>reRequest()) : reRequest()
    }
...

这是我的第一次尝试,在重试请求的同时还加入了重试次数,超过3次就显示异常。然而这个解决方案有个问题,如果有两个并发请求A和B,同时返回401未授权,这时会同时登录,存在网络波动的情况下,B接口可能先重新登录成功,刷新了token,在重新尝试调用B接口时,A接口重新登录成功,刷新了token。接着又是401…401…。当然存在正常按照顺序执行的情况,但更多的是会多刷新几遍token,也就是会多执行几次登录接口。为了解决这个问题,继续优化代码。

let isRefreshing = false
/* 存储请求的数组 */
const refreshSubscribers = []

/* 将所有的请求都push到数组中,其实数组是[function(token){}, function(token){},...] */
function subscribeTokenRefresh(cb) {
  refreshSubscribers.push(cb)
}
...
	if (res.statusCode === 401) {
      // 缓存401的请求,在更新token后再重新执行
      subscribeTokenRefresh(() => {
        return request(...res.requestObj)
      })

      if (!isRefreshing) {
        isRefreshing = true
        Promise.resolve()
          .then(() => login())
          .then(() => {
            // 依次执行缓存数组中的请求
            while (refreshSubscribers.length > 0) {
              const fn = refreshSubscribers.shift()
              try {
                fn && fn()
              } catch (e) {
                throw e
              }
            }
            isRefreshing = false
          })
          .catch(e => {
            throw e
          })
      }
    }
...

这次的优化是将请求缓存起来,在刷新后再重新执行。这样只会调用一次登录。