最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 使用axios阻止多余的请求(007,久违的更新)

    正文概述 掘金(maxlespaul)   2020-11-28   616

    使用axios阻止多余的请求

    使用axios阻止多余的请求(007,久违的更新)

    前言

    在日常的复杂业务中,我们总是调用大量的后端api。为了提升服务器的效率,我们需要尽可能的减少每个用户的平均请求数量

    本文的内容,着重对重复多余的请求做处理,以节约网络资源。比如:

    • 用户高频点击某个发送请求的按钮
    • 本地的定时请求任务,前一个请求还未完成就发送了新的请求

    第二个场景比较罕见,不过在一次开发任务中,糟糕的测试服务器使这个问题严重影响了我的开发体验,印象深刻。

    在服务器资源充足的时候,上述问题一般不会暴露,不过在服务器资源紧张的时候,减少非必要请求可以为服务器争取更多的可用空间。

    本文会展示“阻止多余请求”的工具的实现,最后,会稍微探讨我们通过这个工具还可以做些什么。

    做什么

    如何定义“重复”和“多余”?当我们通过请求获取数据时,我们希望在该请求完成前,不去发送新的相同请求;当我们提交数据时,我们希望提交的数据是最新的,所以需要取消之前的请求。我们把问题展开,发现需求是这样的:

    • 当请求方法为 get 时,当 x 请求未完成时,阻止新触发的 x 请求
    • 当请求方法不为 get 时,当 x 请求未完成时,如果有新的 x 请求被触发,则取消前一个 x 请求,重新发送新的 x 请求,以保证发送的数据是最新的
    • 可以手动清除特定的请求

    怎么做

    使用工具

    我们将会用到 axios 库中的 CancelToken API,这个API专门用于取消某个正在执行的请求,具体参加文档,不再赘述。文档传送门

    整体思路

    先规定这个工具应该放在哪里。作为一个全局的中间件,放在axios的拦截器里是再合适不过了。我们定义两个方法,cancelReqresetReq

    cancelReq 负责标记正在进行的请求、判断是否有相同的请求正在执行;resetReq 负责清除已完成请求的标记

    import { cancelReq, resetReq } from 'cancel-token.js'
    
    // axios.js
    // 请求拦截器
    _axios.interceptors.request.use(
      function(config) {
        // * cancelToken 阻止重复请求
        config = cancelReq(config)
        return config
      },
      function(error) {
        return Promise.reject(error)
      }
    )
    
    // 响应拦截器
    _axios.interceptors.response.use(
      function(response) {
        // * cancelToken 完成请求,清除标记
        resetReq(response.config)
      },
      function(error) {
        if (error.config) {
          resetReq(error.config)
        }
        return Promise.reject(error)
      }
    )
    

    我们将这个工具放在 cancel-token.js 文件中。首先我们需要一个存放“正在执行的请求”的一个队列 reqList ;同时,我们规定一个构造函数 Req,存放一个请求的关键信息,Req 主要存储一个请求的 urlmethod 以及 用于结束该请求的cancel 方法。

    考虑到某些复杂的业务场景,如果上述参数不足以区分每个请求(比如某个页面需要同时发送两次 /get/methodget 请求,分别带上 ?query=a?param=b),这时,我们需要在 Req 构造函数中添加 querydata

    // cancel-token.js
    // * 存放当前正在执行的请求
    const reqList = []
    
    /**
     * * 构造函数 - axios请求的关键对象(url/method/params/data)
     * @param {*} config 当前axios请求的config
     */
    function Req(config) {
      this.url = config.url
      this.method = config.method
      this.cancel = config.cancel
      // * 可能需要添加config.query或config.data
    }
    

    cancelReq 方法的实现

    cancelReq 需要做的是:

    • 将新的请求添加到“正在执行”的队列中
    • 阻止重复的 get 请求
    • 如果有新的提交数据的请求,如 postput,取消之前的那个请求并清除出队列,执行新的请求并添加到队列

    cancelReq 使用到了 CancelToken。我们使用 _cancel 变量去接受 cancel 方法,并将这个方法添加到该请求的 config,这样我们可以随时随地调用 cancel('这个请求结束了') 终止这个请求。

    准备完成后,创建该请求的 Req 实例 _req ,这个 _req 将存放于执行队列 reqList中,如果队列里存在该请求,则按照下方 else {} 函数体中的逻辑执行。

    // cancel-token.js
    import { CancelToken } from 'axios'
    
    // * 阻止请求重复发送
    export function cancelReq(config) {
      const _config = config
      let _cancel
      // * 拿到cancel方法
      _config.cancelToken = new CancelToken(function(cancel) {
    	// 赋值
        _cancel = cancel
        _config.cancel = cancel
      })
      // 创建Req的一个实例
      const _req = new Req(_config)
      // ?注意,findReq 这个方法用于查找这个req实例是否已经存在于执行队列reqList中
      const _index = findReq(_req)
      if (_index === -1) {
      	// 该请求未处于执行状态,将它添加到执行队列,over
        reqList.push(_req)
      } else {
      	// 看来这个请求正在执行
        if (_req.method.toLowerCase() === 'get') {
          // * 如果method 为 get 则阻止后一个请求
          _cancel(`已取消重复发送的请求: ${_config.url}`)
        } else {
          // * 如果不为get 则取消前一个请求,发送新的请求
          reqList[_index].cancel(`已取消重复发送的请求: ${_config.url}`)
          reqList.splice(_index, 1)
        }
      }
      // 最后返回处理完成的config,交给axios请求拦截器
      return _config
    }
    

    我们判断 reqList 中是否有某个 req ,使用的是一个 findReq 方法。下面看一下 findReq 方法的实现。

    /**
     * * 将要发送的请求是否已经存在
     * @param {*} target 将要进行的请求
     * @returns {*} 这个请求的index,-1即没有
     */
    function findReq(target) {
      let _index = -1
      for (const i in reqList) {
        if (reqList[i].url === target.url &&
          reqList[i].method === target.method) {
          // * 还可以添加其他的规则
            _index = i
            break
        }
      }
      return _index
    }
    

    resetReq 方法的实现

    resetReq 需要做的是:

    • 清除完成的请求

    做法与 cancelReq 类似,创建一个 Req 实例,在 reqList 中查找该请求并清除。

    // * 清除完成发送的请求
    export function resetReq(config) {
      const _req = new Req(config)
      const _index = findReq(_req)
      if (_index !== -1) {
        reqList.splice(_index, 1)
      }
    }
    

    手动清除请求的方法

    某些具体的业务的实现可能会需要我们手动清理某些需求,此时我们定义一个 manualCancelReq 方法。这个方法清理掉特定 urlmethod 的请求,并且可以做到清理不至一个匹配到的请求(如果有的话)。这里使用了递归来做到清理多个请求。

    import { findIndex as _findIndex } from 'lodash'
    
    /**
     * * 手动清除某个或某些可能存在的请求(根据url匹配)
     * @param {string} url 请求的url
     * @param {string | null} method 请求的method,可传 null
     * @param {boolean} multi 清除多个请求的就传 true
     */
    export function manualCancelReq(url, method, multi) {
      const _reqIndex = _findIndex(
        reqList,
        Object.assign({ url }, method ? { method } : {})
      )
      if (_reqIndex !== -1) {
        reqList[_reqIndex].cancel()
        reqList.splice(_reqIndex, 1)
        // * 如果是清除多个请求
        if (multi) {
          manualCancelReq(url, method, multi)
        }
      }
    }
    

    好的,那么现在你的axios拥有了阻止多余请求的能力。

    扩展

    基本功能到此为止就实现了。我们可以通过这个工具实现其他的一些功能,比如下面的“当有正在进行的请求时,页面展示loading动画”

    我们需要在每一个请求发起时,也就是 cancelReq 方法中执行 startLoading 方法激活 loading 动画;在请求完成时,也就是 resetReq 方法中执行 stopLoading 方法关闭 loading 动画。

    当然,我们需要做一些判断,不能重复激活动画,也必须在所有请求完成后再关闭动画。同时我们可以为动画激活做一点延时,比如小于300ms的快速请求情况下,不激活动画。

    假设激活和关闭动画的方法分别为 Loading.start()Loading.stop()

    // * 页面是否处于loading状态
    let loadingStatus = false
    // * 定时器
    let loadingTimeout = null
    /**
     * * 触发loading - 延时
     * * 条件:loadingStatus 为 false
     * @param {Number} timeout 激活动画的延时 ms
     */
    export function startLoading(timeout) {
      if (!loadingStatus) {
        loadingTimeout = setTimeout(() => {
          Loading.start()
        }, timeout || 300)
        loadingStatus = true
      }
    }
    
    /**
     * * 结束loading
     */
    export function stopLoading() {
      if (loadingStatus && !reqList.length) {
        clearTimeout(loadingTimeout)
        Loading.stop()
        loadingStatus = false
      }
    }
    

    起源地下载网 » 使用axios阻止多余的请求(007,久违的更新)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元