Axios取消重复请求的方法实例详解

目录
  • 前言
  • 一、如何取消请求
  • 二、如何判断重复请求
  • 三、如何取消重复请求
    • 3.1 定义辅助函数
    • 3.2 设置请求拦截器
    • 3.3 设置响应拦截器
  • 四、CancelToken 的工作原理
  • 五、总结
  • 六、参考资源

前言

在 Web 项目开发过程中,我们经常会遇到重复请求的场景,如果系统不对重复的请求进行处理,则可能会导致系统出现各种问题。比如重复的 post 请求可能会导致服务端产生两笔记录。那么重复请求是如何产生的呢?这里我们举 2 个常见的场景:

  • 假设页面中有一个按钮,用户点击按钮后会发起一个 AJAX 请求。如果未对该按钮进行控制,当用户快速点击按钮时,则会发出重复请求。
  • 假设在考试结果查询页面中,用户可以根据 “已通过”、“未通过” 和 “全部” 3 种查询条件来查询考试结果。如果请求的响应比较慢,当用户在不同的查询条件之前快速切换时,就会产生重复请求。

既然已经知道重复请求是如何产生的,也知道了它会带来一些问题。接下来,阿宝哥将以 Axios 为例,带大家来一起解决重复请求的问题。

一、如何取消请求

Axios 是一个基于 Promise 的 HTTP 客户端,同时支持浏览器和 Node.js 环境。它是一个优秀的 HTTP 客户端,被广泛地应用在大量的 Web 项目中。对于浏览器环境来说,Axios 底层是利用 XMLHttpRequest 对象来发起 HTTP 请求。如果要取消请求的话,我们可以通过调用 XMLHttpRequest 对象上的 abort 方法来取消请求:

let xhr = new XMLHttpRequest();
xhr.open("GET", "https://developer.mozilla.org/", true);
xhr.send();
setTimeout(() => xhr.abort(), 300);

而对于 Axios 来说,我们可以通过 Axios 内部提供的 CancelToken 来取消请求:

const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.post('/user/12345', {
  name: 'semlinker'
}, {
  cancelToken: source.token
})

source.cancel('Operation canceled by the user.'); // 取消请求,参数是可选的

此外,你也可以通过调用 CancelToken 的构造函数来创建 CancelToken,具体如下所示:

const CancelToken = axios.CancelToken;
let cancel;

axios.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
    cancel = c;
  })
});

cancel(); // 取消请求

现在我们已经知道在 Axios 中如何使用 CancelToken 来取消请求了,那么 CancelToken 内部是如何工作的呢?这里我们先记住这个问题,后面阿宝哥将为你们揭开 CancelToken 背后的秘密。接下来,我们来分析一下如何判断重复请求。

二、如何判断重复请求

当请求方式、请求 URL 地址和请求参数都一样时,我们就可以认为请求是一样的。因此在每次发起请求时,我们就可以根据当前请求的请求方式、请求 URL 地址和请求参数来生成一个唯一的 key,同时为每个请求创建一个专属的 CancelToken,然后把 key 和 cancel 函数以键值对的形式保存到 Map 对象中,使用 Map 的好处是可以快速的判断是否有重复的请求:

import qs from 'qs'

const pendingRequest = new Map();
// GET -> params;POST -> data
const requestKey = [method, url, qs.stringify(params), qs.stringify(data)].join('&');
const cancelToken = new CancelToken(function executor(cancel) {
  if(!pendingRequest.has(requestKey)){
    pendingRequest.set(requestKey, cancel);
  }
})

当出现重复请求的时候,我们就可以使用 cancel 函数来取消前面已经发出的请求,在取消请求之后,我们还需要把取消的请求从 pendingRequest 中移除。现在我们已经知道如何取消请求和如何判断重复请求,下面我们来介绍如何取消重复请求。

三、如何取消重复请求

因为我们需要对所有的请求都进行处理,所以我们可以考虑使用 Axios 的拦截器机制来实现取消重复请求的功能。Axios 为开发者提供了请求拦截器和响应拦截器,它们的作用如下:

  • 请求拦截器:该类拦截器的作用是在请求发送前统一执行某些操作,比如在请求头中添加 token 字段。
  • 响应拦截器:该类拦截器的作用是在接收到服务器响应后统一执行某些操作,比如发现响应状态码为 401 时,自动跳转到登录页。

3.1 定义辅助函数

在配置请求拦截器和响应拦截器前,阿宝哥先来定义 3 个辅助函数:

generateReqKey:用于根据当前请求的信息,生成请求 Key;
function generateReqKey(config) {
  const { method, url, params, data } = config;
  return [method, url, Qs.stringify(params), Qs.stringify(data)].join("&");
}

addPendingRequest:用于把当前请求信息添加到pendingRequest对象中;

const pendingRequest = new Map();
function addPendingRequest(config) {
  const requestKey = generateReqKey(config);
  config.cancelToken = config.cancelToken || new axios.CancelToken((cancel) => {
    if (!pendingRequest.has(requestKey)) {
       pendingRequest.set(requestKey, cancel);
    }
  });
}

removePendingRequest:检查是否存在重复请求,若存在则取消已发的请求。

function removePendingRequest(config) {
  const requestKey = generateReqKey(config);
  if (pendingRequest.has(requestKey)) {
     const cancelToken = pendingRequest.get(requestKey);
     cancelToken(requestKey);
     pendingRequest.delete(requestKey);
  }
}

创建好 generateReqKey、addPendingRequest 和 removePendingRequest 函数之后,我们就可以设置请求拦截器和响应拦截器了。

3.2 设置请求拦截器

axios.interceptors.request.use(
  function (config) {
    removePendingRequest(config); // 检查是否存在重复请求,若存在则取消已发的请求
    addPendingRequest(config); // 把当前请求信息添加到pendingRequest对象中
    return config;
  },
  (error) => {
     return Promise.reject(error);
  }
);

3.3 设置响应拦截器

axios.interceptors.response.use(
  (response) => {
     removePendingRequest(response.config); // 从pendingRequest对象中移除请求
     return response;
   },
   (error) => {
      removePendingRequest(error.config || {}); // 从pendingRequest对象中移除请求
      if (axios.isCancel(error)) {
        console.log("已取消的重复请求:" + error.message);
      } else {
        // 添加异常处理
      }
      return Promise.reject(error);
   }
);

由于完整的示例代码内容比较多,阿宝哥就不放具体的代码了。感兴趣的小伙伴,可以访问以下地址浏览示例代码。

完整的示例代码:https://gist.github.com/semlinker/e426780664f0186db434882f1e27ac3a

这里我们来看一下 Axios 取消重复请求示例的运行结果:

从上图可知,当出现重复请求时,之前已发送且未完成的请求会被取消掉。下面我们用一张流程图来总结一下取消重复请求的处理流程:

最后,我们来回答前面留下的问题,即 CancelToken 内部是如何工作的?

四、CancelToken 的工作原理

在前面的示例中,我们是通过调用 CancelToken 构造函数来创建 CancelToken 对象:

new axios.CancelToken((cancel) => {
  if (!pendingRequest.has(requestKey)) {
    pendingRequest.set(requestKey, cancel);
  }
})

所以接下来,我们来分析 CancelToken 构造函数,该函数被定义在 lib/cancel/CancelToken.js 文件中:

// lib/cancel/CancelToken.js
function CancelToken(executor) {
  if (typeof executor !== 'function') {
    throw new TypeError('executor must be a function.');
  }

  var resolvePromise;
  this.promise = new Promise(function promiseExecutor(resolve) {
    resolvePromise = resolve;
  });

  var token = this;
  executor(function cancel(message) { // 设置cancel对象
    if (token.reason) {
      return; // Cancellation has already been requested
    }
    token.reason = new Cancel(message);
    resolvePromise(token.reason);
  });
}

由以上代码可知,cancel 对象是一个函数,当我们调用该函数后,会创建 Cancel 对象并调用 resolvePromise 方法。该方法执行后,CancelToken 对象上 promise 属性所指向的 promise 对象的状态将变为 resolved。那么这样做的目的是什么呢?这里我们从 lib/adapters/xhr.js 文件中找到了答案:

// lib/adapters/xhr.js
if (config.cancelToken) {
  config.cancelToken.promise.then(function onCanceled(cancel) {
    if (!request) { return; }
    request.abort(); // 取消请求
    reject(cancel);
    request = null;
  });
}

看完上述的内容,可能有的小伙伴还不是很能理解 CancelToken 的工作原理,所以阿宝哥又画了一张图来帮助大家理解 CancelToken 的工作原理:

五、总结

本文介绍了在 Axios 中如何取消重复请求及 CancelToken 的工作原理,在后续的文章中,阿宝哥将会介绍在 Axios 中如何设置数据缓存,感兴趣的小伙伴不要错过哟。如果你想了解 Axios 中 HTTP 拦截器及 HTTP 适配器的设计与实现,可以阅读77.9K 的 Axios 项目有哪些值得借鉴的地方 这篇文章。

到此这篇关于Axios取消重复请求的文章就介绍到这了,更多相关Axios取消重复请求内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

六、参考资源

  • Github - Axios
  • MDN - XMLHttpRequest
  • 77.9K 的 Axios 项目有哪些值得借鉴的地方
(0)

相关推荐

  • axios取消请求与避免重复请求

    目录 起源 现状 取消请求 cancelToken 修改后的请求方法 避免重复请求 总结 起源 某个页面需要下载全部数据的功能,下载数据量大,接口延迟长..... 某个页面加载初始数据量延长长,但单个检索快速,出现初始数据加载中时,检索接口返回,初始数据后续返回覆盖了检索数据的展示.... 这些情况需要我们: 能够手动取消/终止请求Request. 某些页面接口同时只能有一个在请求. 现状 系统基于老哥花裤衩开源的vue-element-admin做的二次开发,其中的请求采用的是axios,其中

  • vue axios重复点击取消上一次请求封装的方法

    使用场景 重复点击或者多tab标签使用一个视图等(当然也可以用加载中或者透明背景禁止请求中再次点击) 封装代码 来自于互联网 let pending = []; //声明一个数组用于存储每个请求的取消函数和axios标识 let cancelToken = axios.CancelToken; let removePending = (config) => { for(let p in pending){ if(pending[p].u === config.url + '&' + conf

  • 项目中如何使用axios过滤多次重复请求详解

    目录 一.前言: 这个情况下,我们通常的做法有两种: 二.CancelToken类 最终效果 总结 一.前言: 我们在web应用开发过程当中,经常会遇到一个时刻发起了多个请求的场景 这个情况下,我们通常的做法有两种: 可以在请求时show一个loading,阻止用户操作. 或者人为加个变量,做一个请求的节流 我们的项目中,目前大部分情况也是采用以上两种方式做的.今天来介绍一个新的方式. 二.CancelToken类 我们之前实例化一个Promise,这个对象是否成功与否,是无法在函数外部决定的,

  • axios如何取消重复无用的请求详解

    前言 在开发中,经常会遇到接口重复请求导致的各种问题. 对于重复的get请求,会导致页面更新多次,发生页面抖动的现象,影响用户体验. 对于重复的post请求,会导致在服务端生成两次记录(例如生成两条订单记录). 如果当前页面请求还未响应完成,就切换到了下一个路由,那么这些请求直到响应返回才会中止. 无论从用户体验或者从业务严谨方面来说,取消无用的请求确实是需要避免的. 当然我们可以通过页面loading来避免用户进行下一次的操作,但本文只讨论单纯的如何取消这些无用的请求. axios 的 can

  • Axios取消重复请求的方法实例详解

    目录 前言 一.如何取消请求 二.如何判断重复请求 三.如何取消重复请求 3.1 定义辅助函数 3.2 设置请求拦截器 3.3 设置响应拦截器 四.CancelToken 的工作原理 五.总结 六.参考资源 前言 在 Web 项目开发过程中,我们经常会遇到重复请求的场景,如果系统不对重复的请求进行处理,则可能会导致系统出现各种问题.比如重复的 post 请求可能会导致服务端产生两笔记录.那么重复请求是如何产生的呢?这里我们举 2 个常见的场景: 假设页面中有一个按钮,用户点击按钮后会发起一个 A

  • vue全局使用axios的方法实例详解

    在vue项目开发中,我们使用axios进行ajax请求,很多人一开始使用axios的方式,会当成vue-resoure的使用方式来用,即在主入口文件引入import VueResource from 'vue-resource'之后,直接使用Vue.use(VueResource)之后即可将该插件全局引用了,所以axios这样使用的时候就报错了,很懵逼. 仔细看看文档,就知道axios 是一个基于 promise 的 HTTP 库,axios并没有install 方法,所以是不能使用vue.us

  • Oracle表中重复数据去重的方法实例详解

    Oracle表中重复数据去重的方法实例详解 我们在项目中肯定会遇到一种情况,就是表中没有主键 有重复数据 或者有主键 但是部分字段有重复数据 而我们需要过滤掉重复数据 下面是一种解决方法 delete from mytest ms where rowid in (select aa.rid from (select rowid as rid, row_number() over(partition by s.name order by s.id) as nu from mytest s) aa

  • java 请求跨域问题解决方法实例详解

    java 请求跨域问题解决方法实例详解 新建Util类,在Util中添加下面方法: /* * response请求跨域公共设置 */ public static HttpServletResponse SetHttpServletResponse( HttpServletResponse response) { response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader(&qu

  • jQuery中ajax - get() 方法实例详解

    在jquery中使用get,post和ajax方法给服务器端传递数据,在上篇文章给大家分享了jquery中ajax-post()方法实例,下面通过本文继续学习jQuery中ajax - get() 方法,具体介绍请看下文. jQuery Ajax 参考手册 实例 使用 AJAX 的 GET 请求来改变 div 元素的文本: $("button").click(function(){ $.get("demo_ajax_load.txt", function(resul

  • 微信小程序之网络请求简单封装实例详解

    微信小程序之网络请求简单封装实例详解 在微信小程序中实现网络请求相对于Android来说感觉简单很多,我们只需要使用其提供的API就可以解决网络请求问题. 普通HTTPS请求(wx.request) 上传文件(wx.uploadFile) 下载文件(wx.downloadFile) WebSocket通信(wx.connectSocket) 为了数据安全,微信小程序网络请求只支持https,当然各个参数的含义就不在细说,不熟悉的话可以:可以去阅读官方文档的网络请求api,当我们使用request

  • Android 网络请求框架Volley实例详解

    Android 网络请求框架Volley实例详解 首先上效果图 Logcat日志信息on Reponse Volley特别适合数据量不大但是通信频繁的场景,像文件上传下载不适合! 首先第一步 用到的RequetQueue RequestQueue.Java RequestQueue请求队列首先得先说一下,ReuqestQueue是如何对请求进行管理的...RequestQueue是对所有的请求进行保存...然后通过自身的start()方法开启一个CacheDispatcher线程用于缓存调度,开

  • springboot config 拦截器使用方法实例详解

    本文介绍Spring-Boot中使用拦截器,一般在拦截器中处理跨域处理,允许跨域访问项目,拦截器使用详细资料请查阅官网. 实现自定义拦截器步骤: 1.创建一个类并实现HandlerInterceptor接口. 2.创建一个Java类继承WebMvcConfigurerAdapter,并重写 addInterceptors 方法. 2.将自定义的拦截器交由spring管理,然后将对像手动添加到拦截器链中(在addInterceptors方法中添加). 创建拦截器类 package com.exam

  • Springmvc ajax跨域请求处理方法实例详解

    上次给一个网站写网站  前后端分离 最后跪在ajax跨域上面了  自己在网上找了个方法  亲试可用  记录一下 写一个类  继承HandlerInterceptorAdapter package com.util; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.handler.Ha

  • IOS自带Email的两种方法实例详解

    IOS自带Email的两种方法实例详解 IOS系统框架提供的两种发送Email的方法:openURL 和 MFMailComposeViewController.借助这两个方法,我们可以轻松的在应用里加入如用户反馈这类需要发送邮件的功能. 1.openURL 使用openURL调用系统邮箱客户端是我们在IOS3.0以下实现发邮件功能的主要手段.我们可以通过设置url里的相关参数来指定邮件的内容,不过其缺点很明显,这样的过程会导致程序暂时退出.下面是使用openURL来发邮件的一个小例子: #pr

随机推荐