美文网首页
Axios如何取消重复请求

Axios如何取消重复请求

作者: 前端技术驿站 | 来源:发表于2021-07-06 22:11 被阅读0次

在实际开发中,我们需要对用户发起的重复请求进行拦截处理,比如用户快速点击提交按钮。

对于重复的 get 请求,会导致页面更新多次,发生页面抖动的现象,影响用户体验;对于重复的 post 请求,会导致在服务端生成两次记录(例如生成两条订单记录)。

无论从用户体验或者从业务严谨方面来说,取消无用的请求是需要避免的。

一、一般处理方式

我们可以在用户即将发送请求,但还未发送请求时给页面添加一个 loading 效果,提示数据正在加载,loading 会阻止用户继续操作。

这种方式在大部分情况下是可行的,但是在某些情况下却不奏效,比如在 loading 显示之前,用户就已经触发了两次请求的情况。

二、Axios 拦截器统一处理

重复发送的请求的场景很多,我们需要在一个公共的地方对请求响应进行处理,Axios 拦截器就闪亮登场了。
Axios 拦截器包括请求拦截器和响应拦截器,可以在请求发送前或响应后进行拦截处理,用法如下:

// 添加请求拦截器
axios.interceptors.request.use(
  function (config) {
    // 在发送请求之前做些什么
    return config;
  },
  function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  }
);

// 添加响应拦截器
axios.interceptors.response.use(
  function (response) {
    // 对响应数据做点什么
    return response;
  },
  function (error) {
    // 对响应错误做点什么
    return Promise.reject(error);
  }
);

那么,如何进行拦截呢?也就是如何取消用户的请求,将它扼杀在摇篮里...

2.1、如何取消请求

众所周知,浏览器是通过 XMLHttpRequest 对象进行 http 通信的,如果要取消请求的话,我们可以通过调用 XMLHttpRequest 对象上的 abort 方法来取消请求。

let xhr = new XMLHttpRequest();
xhr.open("GET", "http://www.shanzhonglei.com/", true);
xhr.send();
setTimeout(() => xhr.abort(), 300);

Axios是一个主流的http请求库,它提供了两种取消请求的方式。

第一种,通过axios.CancelToken.source生成取消令牌token和取消方法cancel。

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

axios.get('/user/12345', {
  cancelToken: source.token
}).catch(function(thrown) {
  if (axios.isCancel(thrown)) {
    console.log('Request canceled', thrown.message);
  } else {
    // handle error
  }
});

// cancel the request (the message parameter is optional)
source.cancel('Operation canceled by the user.');

第二种,通过axios.CancelToken构造函数生成取消函数。

const CancelToken = axios.CancelToken;
let cancel;

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

// cancel the request
cancel();

需要注意的是在catch中捕获异常时,应该使用axios.isCancel()判断当前请求是否是主动取消的,以此来区分普通的异常逻辑。

知道了如何取消请求就好办了,如果两个请求是相同的,那么我们就可以对后一个请求进行拦截操作。

2.2、判断重复请求

我们可以把每个请求的方法、url 和参数组合成一个字符串,作为该请求的唯一标识 key,与此同时,为对应的 key 生成一个 CancelToken 以备取消当前的请求。把 key 和对应的 cancel 函数以键值对的形式保存在 Map 对象中。

const pendingRequest = new Map();
const requestKey = [
  method,
  url,
  JSON.stringify(params),
  JSON.stringify(data),
].join("&");
const cancelToken = new CancelToken(function executor(cancel) {
  if (!pendingRequest.has(requestKey)) {
    pendingRequest.set(requestKey, cancel);
  }
});

定义pendingRequests 为 map 对象的目的是为了方便我们查询它是否包含某个 key,以及添加和删除 key。

在请求拦截器中,会检查pendingRequests 对象中是否包含当前请求的 requestKey,如果重复,就cancel拦截掉当前请求,如果不重复,则将requestKey 添加到 pendingRequests 对象中。

2.3 具体实现

我们先来生成几个辅助函数:

generateReqKey:用于根据当前请求的信息,生成请求 Key

function generateReqKey(config) {
  const { method, url, params, data } = config;
  return [method, url, JSON.stringify(params), JSON.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);
  }
}

clearPending 清空 pending 中的请求(在路由跳转时调用)

function clearPending() {
  for (const [requestKey, cancelToken] of pendingRequest) {
    cancelToken(requestKey)
  }
  pendingRequest.clear()
}

实操来了...

请求拦截器

axios.interceptors.request.use(
  function (config) {
    removePendingRequest(config); // 检查是否存在重复请求,若存在则取消已发的请求
    addPendingRequest(config); // 把当前请求信息添加到pendingRequest对象中
    return config;
  },
  (error) => {
    // 这里出现错误可能是网络波动造成的,清空 pendingRequests 对象
    pendingRequests.clear();
    return Promise.reject(error);
  }
);

响应拦截器

在这里,说明请求已经结束了,状态已经变成pending,这时需要把它从pendingRequests删除。

axios.interceptors.response.use(
  (response) => {
    removePendingRequest(response.config); // 从pendingRequest对象中移除请求
    return response;
  },
  (error) => {
    removePendingRequest(error.config || {}); // 从pendingRequest对象中移除请求
    if (axios.isCancel(error)) {
      console.warn(error);
      return Promise.reject(error);
    } else {
      // 添加其它异常处理
    }
    return Promise.reject(error);
  }
);

最后,我们要在页面切换之前取消上一个路由中未完成的请求,清空缓存的pendingRequest对象。

router.beforeEach((to, from, next) => {
  clearPending();
  // ...
  next();
});

最后

关注公众号【前端技术驿站】让我们共同进步吧!我整理了一些项目实战视频,欢迎来学习!

相关文章

  • Axios如何取消重复请求

    在 Web 项目开发过程中,我们经常会遇到重复请求的场景,如果系统不对重复的请求进行处理,则可能会导致系统出现各种...

  • Axios如何取消重复请求

    在实际开发中,我们需要对用户发起的重复请求进行拦截处理,比如用户快速点击提交按钮。 对于重复的 get 请求,会导...

  • Axios 取消重复请求

    有什么用? 当用户频繁点击在短时间内发送多个 ajax 请求,但是由于网络原因服务器数据无法及时响应返回,这时候,...

  • axios取消重复请求

    在开发中,经常会遇到接口重复请求导致的各种问题。 对于重复的get请求,会导致页面更新多次,发生页面抖动的现象,影...

  • axios取消重复请求

    当第一个axios发起连续请求的时候,这时候我们就需要取消第二、第三个请求,因为他们都是同一个请求发起。 来看一下...

  • 使用axios如何取消重复请求

    axios官网文档上取消请求的两种方式 使用CancelToken.source工厂创建一个取消令牌: 还可以通过...

  • axios如何取消接口请求

    vue项目,如何在axios中取消已经发送的请求呢? 原生js的abort()这个方法 在axios中取消接口请求...

  • axios重复请求处理 CancelToken 取消请求

    CancelToken 介绍 ​ axios中用于取消请求的方法 使用 可以使用 CancelToken.so...

  • 基于 axios 实现取消重复请求

    Axios 是一个基于 Promise 的 HTTP 客户端,同时支持浏览器和 Node.js 环境。它是一个优秀...

  • axios取消重复请求实现

    在我们日常开发中,有这样一种场景必须要进行处理,那就是在提交表单的时候,如果很快的重复点击两次,会造成重复请求,第...

网友评论

      本文标题:Axios如何取消重复请求

      本文链接:https://www.haomeiwen.com/subject/oldnultx.html