c

crazyu

V1

2022/10/20阅读:23主题:默认主题

uni.request封装、401重定向、首页Token过期,取消接口请求、限制请求数量

普通人的星光灿烂,大多数人的生活模板,加油

最近群友提出一个需求,用户在首页的时候调用接口,

  1. 接口请求有上线,超出的部分在已完成之后继续请求;
  2. 接口token失效了,或者是无权限,如何在第一次发现接口401的情况下,取消剩下的接口请求,并且重定向到401,

其中的难点主要是在如何取消请求,我查阅了uni.request的文章,它提供了跟原始XHR一样的中止请求的方法abort();

解决方案(思路)

  1. 设置最大请求数limit,当请求数量超出的部分,将请求放在一个数组队列里面waitingList,等待,当前面的请求完成的时候,每次都去取出waitingList第0个元素,如果有请求,那么就执行,如果没有,就return
  2. 正在执行的请求都会存入excutingList执行队列当中,当发现接口的返回code == 401时,将剩下的接口都执行abort()方法,即中断接口请求,然后在将当前页面的地址重定向到login页面

流程图如下:

uni.request封装逻辑图.drawio.png
uni.request封装逻辑图.drawio.png

如果能够真正理解流程图的话,我想实现实际是简单的,可以自己手动尝试封装,如果有不懂的部分可以查看以下的代码:

import Config from '@/common/config.js';

const waitingList = [];  // 等待请求队列
const excutingList = []; // 执行队列
const limit = 10;        // 最大请求数

const excuteWaitingList = () => {
  const func = waitingList[0];
  if (typeof func === 'function') {
    func();
  }
  waitingList.splice(0, 1);
};

const requestPromise = (
  resolve,
  reject,
  url,
  method = 'get',
  params,
  callback
) => {
  const token = Config.token || uni.getStorageSync('token');
  let headers = {};
  if (token) {
    headers['Authorization'] = 'Bearer ' + token; // 让每个请求携带自定义token 请根据实际情况自行修改
  }
  let success = false;
  const baseUrl = Config.baseUrl + Config.requestPrefix;
  const requestTask = uni.request({
    url: url.startsWith('https://') ? url : baseUrl + url, //仅为示例,并非真实接口地址。
    method: method ? method.toUpperCase() : '',
    header: headers,
    data: params,
    success: (res) => {
      const result = res;
      if (url.startsWith('https://')) {
        resolve(result.data);
      }
      let errorMsg = '请求有误';
      if (result && result.data) {
        if (result.data.code === 200) {
          success = true;
          resolve(result.data);
        } else if (result.data.code === 401) {
          if (window.location.href.includes('/pages/dashboard-login/index')) {
            uni.reLaunch({
              url: '/pages/dashboard-login/index',
            });
            Config.clearAllRequest();
          } else
            if (uni.commonData.currentPage !== 'login') {
              uni.reLaunch({
                url: '/pages/login/index',
              });
              Config.clearAllRequest();
            }
          reject(errorMsg);
        } else {
          errorMsg = result.data.msg || errorMsg;
          reject(errorMsg);
          // reject(result.data)
        }
      } else {
        reject(errorMsg);
      }
    },
    fail (res) {
      console.error(res, 'fail')
      reject('服务异常');
    },
    complete () {
      if (success) {
        resolve(true);
      } else {
        resolve(false);
      }
      let index = excutingList.indexOf(requestTask);
      excutingList.splice(index, 1);
      if (typeof callback === 'function') {
        callback();
      }
      // console.log('complete')
      // console.log(tasks, 'tasks')
      // console.log(waitingList, 'waitingList')
      // console.log(excutingList, 'excutingList')
      excuteWaitingList();
    },
  });
  excutingList.push(requestTask);
};

export default function ajaxRequest (url, method = 'get', params) {
  const promiseObj = new Promise((resolve, reject) => {
    if (excutingList.length > limit) {
      waitingList.push((callBack) => {
        requestPromise(resolve, reject, url, method, params, callBack);
      });
    } else {
      requestPromise(resolve, reject, url, method, params);
    }
  });
  return promiseObj;
}

Config.clearAllRequest = () => {
  excutingList.map((item) => {
    if (item && item.abort) {
      item.abort();
    }
  });
  waitingList.length = 0;
  excutingList.length = 0;
}

课后作业

思考下如果使用Axios要如何实现以上的需求,欢迎小伙伴在评论区评论以及说下自己的思路,哈哈

写在最后

我是crazyu,一位前端开发工程师。

  • 文中如有错误,欢迎在评论区指正,如果这篇文章帮到了你,欢迎点赞和关注😊
  • 本文首发于微信公众号:crazyu 前端,未经许可禁止转载

分类:

前端

标签:

JavaScript

作者介绍

c
crazyu
V1