百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程文章 > 正文

停止javascript的ajax请求,取消axios请求,取消reactfetch请求

qiyuwang 2024-10-21 09:39 17 浏览 0 评论

一、Ajax

原生里可以通过XMLHttpRequest对象上的abort方法来中断ajax。注意abort方法不能阻止向服务器发送请求,只能停止当前ajax请求。

停止javascript的ajax请求有两种方式

1. 设置超时时间让ajax自动断开

2. 手动去停止ajax请求,

核心是调用XMLHttpRequest对象上的abort方法

我们以jquery举例说明:

jquery的ajax对象的abort方法:调用abort后jquery会执行error的方法,抛出abort的异常信息,此时即可执行我们中断ajax后的操作。

var ajax = $.ajax({
'error':function(jqXHR, textStatus, errorThrown){
if(errorThrown != 'abort'){
//ajax被调用abort后执行的方法
alert('您的ajax方法被停止了');
        }
    }
})
 
ajax.abort();//停止ajax

原生JS

xmlHttp.open("POST","Url",true);
xmlHttp.onreadystatechange=function(){
 ...//得到响应之后的操作
}
xmlHttp.send();
//设置3秒钟后检查xmlHttp对象所发送的数据是否得到响应.
setTimeout("CheckRequest()","3000");
function CheckRequest(){
   //为4时代表请求完成了
   if(xmlHttp.readyState!=4){
     alert('数据响应超时');
     //关闭请求
     xmlHttp.close();
   }
}

完整板:

<div class="page" id="app">
    <button  class="get-msg">获取数据</button>
    <button  class="cancel">取消获取</button>
  </div>
<script>
  var currentAjax = null
    $('.get-msg').click(function () {
      currentAjax = $.ajax({
        type: 'GET',
        url: 'http://jsonplaceholder.typicode.com/comments',
        success: function (res) {
          console.log(res)
        },
        error: function (err) {
          console.log("获取失败")
        }
      })
    })
    $('.cancel').click(function () {
      if (currentAjax) {
        currentAjax.abort()
      }
    })
</script>

切记:不可用abort方法来作为终止对服务器的请求操作,只有当做在前端页面立刻停止执行ajax成功后的方法,因为你执行abort方法后,ajax很可能已经对服务端发送了请求,只是还未返回回馈信息而已。

二、Axios:

关键词:CancelToken、source、source.token

应用场景:tab页频繁切换,页面的切换,用于终止正在挂起的请求

axios 的 cancelToken

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

项目的如何应用

  • 配置参数
axios({
	method: 'post',
	url: url,
	withCredentials: false,
	params: params,
	data: data,
	cancelToken:data.cancelToken,
	headers: headerParam
}).then(resp => {
	resolve(resp)
}).catch(error => {
	reject(error)
})

在调用请求那传入令牌cancelToken:data.cancelTokendata为传入的参数

调用接口前,传入上一步需要的令牌

let CancelToken = this.axios.CancelToken;this.source = CancelToken.source();data.cancelToken = this.source.token;

axios 在 main.js中挂载到了 实例中, data data 为请求的参数,this.source中有token令牌和取消请求的cancel方法

终止请求

cancelRequest(){
   this.source.cancel("异常信息,选填")
},

在发起新的请求的时候,执行一下this.source.cancel()即可终止正在挂起的请求。

完整版:

<body>
  <div class="page" id="app">
    <button @click="getMsg" class="get-msg">获取数据</button>
    <button @click="cancelGetMsg" class="cancel">取消获取</button>
    <ul>
      <li v-for="item in items">{{item.name}}</li>
    </ul>
  </div>
  <script>
  var app = new Vue({
    el: '#app',
    data: {
      message: 'Hello Vue!',
      items: [],
      cancel: null
    },
    methods: {
      getMsg () {
        let CancelToken = axios.CancelToken
        let self = this
        axios.get('http://jsonplaceholder.typicode.com/comments', {
          cancelToken: new CancelToken(function executor(c) {
            self.cancel = c
            console.log(c)
            // 这个参数 c 就是CancelToken构造函数里面自带的取消请求的函数,这里把该函数当参数用
          })
        }).then(res => {
          this.items = res.data
        }).catch(err => {
          console.log(err)
        })


        //手速够快就不用写这个定时器了,点击取消获取就可以看到效果了
        setTimeout(function () {
          //只要我们去调用了这个cancel()方法,没有完成请求的接口便会停止请求
          self.cancel()
        }, 100)
      },
      //cancelGetMsg 方法跟上面的setTimeout函数是一样的效果,因为手速不够快,哦不,是因为网速太快,导致我来不及点取消获取按钮,数据就获取成功了
      cancelGetMsg () {
        // 在这里去判断你的id 1 2 3,你默认是展示的tab1,点击的时候不管你上一个请求有没有执行完都去调用这个cancel(),
        this.cancel()
      }
    }
  })
  </script>
</body>


axios 的 cancelToken

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
  }
});

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

// 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()判断当前请求是否是主动取消的,以此来区分普通的异常逻辑。

封装取消请求逻辑

上面有两种取消请求,用哪种都是可以的,这里使用第二种。

取消请求主要有两个场景:

  • 当请求方式method,请求路径url,请求参数(getparamspostdata)都相同时,可以视为同一个请求发送了多次,需要取消之前的请求
  • 当路由切换时,需要取消上个路由中未完成的请求

我们封装几个方法:

// 声明一个 Map 用于存储每个请求的标识 和 取消函数
const pending = new Map()
/**
 * 添加请求
 * @param {Object} config 
 */
const addPending = (config) => {
  const url = [
    config.method,
    config.url,
    qs.stringify(config.params),
    qs.stringify(config.data)
  ].join('&')
  config.cancelToken = config.cancelToken || new axios.CancelToken(cancel => {
    if (!pending.has(url)) { // 如果 pending 中不存在当前请求,则添加进去
      pending.set(url, cancel)
    }
  })
}
/**
 * 移除请求
 * @param {Object} config 
 */
const removePending = (config) => {
  const url = [
    config.method,
    config.url,
    qs.stringify(config.params),
    qs.stringify(config.data)
  ].join('&')
  if (pending.has(url)) { // 如果在 pending 中存在当前请求标识,需要取消当前请求,并且移除
    const cancel = pending.get(url)
    cancel(url)
    pending.delete(url)
  }
}
/**
 * 清空 pending 中的请求(在路由跳转时调用)
 */
export const clearPending = () => {
  for (const [url, cancel] of pending) {
    cancel(url)
  }
  pending.clear()
}

MapES6中一种新型的数据结构,本身提供了诸多方法,方便操作,适合当前场景。如果不熟悉的可以查看ECMAScript 6 入门。

在给config.cancelToken赋值的时候,需要判断当前请求是否已经在业务代码中使用了cancelToken

qs是一个专门用来转换对象和字符串参数的库,最初是由 TJ 创建并维护的,也是axios推荐使用的参数序列化库。这里我们的目的只是单纯的将参数对象转换为字符串方便拼接。

Map结构默认部署了Symbol.iterator属性,可以使用for...of循环直接获取键名和键值,当然你也可以使用for...in循环。

在 axios 拦截器中使用

主要的方法已经写好了,只需要添加到axios拦截器中就可以了。

axios.interceptors.request.use(config => {
  removePending(options) // 在请求开始前,对之前的请求做检查取消操作
  addPending(options) // 将当前请求添加到 pending 中
  // other code before request
  return config
}, error => {
  return Promise.reject(error)
})

axios.interceptors.response.use(response => {
  removePending(response) // 在请求结束后,移除本次请求
  return response
}, error => {
  if (axios.isCancel(error)) {
    console.log('repeated request: ' + error.message)
  } else {
    // handle error code
  }
  return Promise.reject(error)
})

clearPending()方法添加到vue路由钩子函数中

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

三、fetch

以下是取消 Fetch 请求的基本步骤:

const controller = new AbortController();
const { signal } = controller;

fetch("http://localhost:8000", { signal }).then(response => {
    console.log(`Request 1 is complete!`);
}).catch(e => {
    console.warn(`Fetch 1 error: ${e.message}`);
});

// Abort request
controller.abort();

abort 调用时发生 AbortError,因此你可以通过比较错误名称来侦听 catch 中的中止操作。

}).catch(e => {
    if(e.name === "AbortError") {
        // We know it's been canceled!
    }
});

将相同的信号传递给多个 fetch 调用将会取消该信号的所有请求:

const controller = new AbortController();
const { signal } = controller;

fetch("http://localhost:8000", { signal }).then(response => {
    console.log(`Request 1 is complete!`);
}).catch(e => {
    console.warn(`Fetch 1 error: ${e.message}`);
});

fetch("http://localhost:8000", { signal }).then(response => {
    console.log(`Request 2 is complete!`);
}).catch(e => {
    console.warn(`Fetch 2 error: ${e.message}`);
});

// Wait 2 seconds to abort both requests
setTimeout(() => controller.abort(), 2000);

样板:

function abortableFetch(request, opts) {
  const controller = new AbortController();
  const signal = controller.signal;

  return {
    abort: () => controller.abort(),
    ready: fetch(request, { ...opts, signal })
  };
}

说实话,我对取消 Fetch 的方法并不感到兴奋。在理想的世界中,通过 Fetch 返回的 Promise 中的 .cancel() 会很酷,但是也会带来一些问题。无论如何,我为能够取消 Fetch 调用而感到高兴,你也应该如此!

使用AbortController来取消 fetch

AbortController 是 JavaScript 的最新版本中的特性,它是在 fetch 被实现之后出现的。 更好的消息是所有现代浏览器都支持它。

AbortController 包含一个 abort 方法。 它还包含一个可以传递给fetchsignal属性。 当调用 AbortController.abort 时,fetch请求就会被取消。

让我们在 getCharacterfetch请求中使用 AbortController 及其signal属性:

function getCharacter(id: number) {
  // 获取AbortController实例
  const controller = new AbortController();
  // 获取 signal属性
  const signal = controller.signal;
  const promise = new Promise(async (resolve) => {
    const response = await fetch(`https://swapi.dev/api/people/${id}/`, {
      method: "get",
      // 将 signal作为fetch的参数之一
      signal,
    });
    const data = await response.json();
    assertIsCharacter(data);
    resolve(data);
  });
  // 设置一个 取消函数
  promise.cancel = () => controller.abort();
  return promise;
}

我们从getCharacter函数中删除了async关键字,并将现有代码包装在一个新的Promise中。 当请求到数据后,我们使用resolve将数据抛出去。 我们在Promise中添加了一个cancel方法,该方法调用了AbortController.abort

包含 cancel 方法的 PromisegetCharacter 中被返回,以便我们在业务代码中可以使用它来取消请求。

保存代码查看界面效果,发现有一个类型错误:

// - Property 'cancel' does not exist on type 'Promise<unknown>'
promise.cancel = () => controller.abort();

让我们为Promisecancel 方法创建一个类型

interface PromiseWithCancel<T> extends Promise<T> {
  cancel: () => void;
}
// 使用 然使用类型断言来解决前面的类型错误
function getCharacter(id: number) {
  ...
  (promise as PromiseWithCancel<Character>).cancel = () => controller.abort();
  return promise as PromiseWithCancel<Character>;
}

在 React 组件中使用getCharacter

我们将把getCharacter返回的 promise 存储在一个名为query的状态变量中。

export function App() {
  const [status, setStatus] = React.useState<"loading" | "loaded" | "cancelled">("loading");
  const [data, setData] = React.useState<Character | undefined>(undefined);
  const [query, setQuery] = React.useState<PromiseWithCancel<Character> | undefined>(undefined);
  React.useEffect(() => {
    const q = getCharacter(1);
    setQuery(q);
    q.then((character) => {
      setData(character);
      setStatus("loaded");
    });
  }, []);
  ...

现在,当点击取消按钮的时候,我们调用 promise 中的cancle方法

<button
  onClick={() => {
    query?.cancel();
    setStatus("cancelled");
  }}
>
  Cancel
</button>

点击取消按钮后吗,我们看到发现’Cancelled‘文案被渲染出来了


编辑

添加图片注释,不超过 140 字(可选)


再通过谷歌开发者工具查看网络请求,发现请求也被取消了

?

编辑切换为居中

添加图片注释,不超过 140 字(可选)

捕获"取消请求"发生的错误

让我们再看看控制台,当请求被取消后,有错误被抛出了


?

编辑切换为居中

添加图片注释,不超过 140 字(可选)

我们可以使用 try catch 来包裹请求以便捕获错误

const promise = new Promise(async (resolve) => {
  try {
    const response = await fetch(`https://swapi.dev/api/people/${id}/`, {
      method: "get",
      signal,
    });
    const data = await response.json();
    assertIsCharacter(data);
    resolve(data);
  } catch (ex: unknown) {
    if (isAbortError(ex)) {
      console.log(ex.message);
    }
  }
});

isAbortError类型的函数如下

function isAbortError(error: any): error is DOMException {
  if (error && error.name === "AbortError") {
    return true;
  }
  return false;
}

现在当我们再次点击取消按钮,我们会在控制台收到一条消息提示而不是一个错误


?

编辑

添加图片注释,不超过 140 字(可选)


总结

可以将AbortController中的signal属性传递给fetch。 然后可以调用AbortController.abort取消请求。

取消 fetch 会引发一个错误,但可以使用try catch将其捕获。

相关推荐

windows开启telnet服务,检测远程服务端口是否可以连通

本文介绍windwos开启telnet服务,telnet服务一般可以用于检测远程主机的某个端口服务是否可以连通,在日常的工作中,我们经常会遇到在本地的windows检测远程服务端口是否可以连通。win...

仅在Web登录新华三交换机条件下启用设备Telnet登录方式

概述Web登录新华三交换机可以在“网络-服务”页面中启用设备Telnet服务或SSH服务,也可以在“设备-管理员”设置管理员用户的可用服务,然而,在设备Web页面中,无法设置lineVTY用户线【l...

思科交换机,路由器如何关闭telnet 开启ssh服务

SSH为建立在应用层基础上的安全协议。SSH是目前较可靠,专为远程登录会话和其他网络服务提供安全性的协议。利用SSH协议可以有效防止远程管理过程中的信息泄露问题。今天我们就来说说思科交换机,路...

智能化弱电行业常用的DOS命令,掌握了你也能成为...

前言在做智能化弱电项目时,前端摄像头设备安装结束后,我们会对网络摄像头进行调试,调试过程中会遇到前端摄像头没有图像或者图像出来了画面卡顿的现象。我们会采用ping命令来测试网络的连通性和网络承载能力。...

「干货」eNSP模拟器之配置Telnet登录

配置说明:配置Telnet,使R2(模拟PC)通过SW1登录到R1进行管理和配置。操作步骤:system-view##进入系统视图[Huawei]sysnameR1##改名为R1[R1]int...

win11开启telnet服务怎么操作 win11打开telent指令是什么

telnet服务是我们在进行远程连接的时候,必须要打开的一项功能。但是有不少用户们不清楚在windows11系统中怎么开启telnet服务。今天小编就使用详细的图文教程,来给大家说明一下打开telen...

华三(H3C)交换机Telnet的远程登陆

一,配置交换机管理IP[SW1]vlan20//创建管理vlan[SW1]interfacevlan20//进入vlan接口[SW1-Vlanif20]ipaddress192.168....

win10 telnet命令怎么查看端口是否打开

可能大家也会遇到这个问题,win10telnet命令查看端口是否打开的步骤是什么?具体方法如下:1、键盘输入快捷键WIN+R,打开运行窗口。2、输入cmd,点击确定按钮。3、弹出cmd命令行窗...

Windows 7如何打开Telnet功能(win7系统打开telnet)

Windows7默认安装后是没有开启telnet客户端功能的,例如,我们在开始菜单中输入cmd,然后使用telnet命令,会弹出下图提示:‘telnet’不是内部或外部命令,也不是可运行程序或批处理文...

为锐捷路由器交换机开启web和telnet,实现轻松管理

笔者上一篇文章写了关于锐捷二层交换机配置教程,那么接下来讲一下锐捷的路由交换设备配置web、telnet技巧。同样,今天的教程也是基于命令行,比较简单,适合新手小白进行学习。准备工作配置前准备:con...

一文学会telnet命令的用途和使用方法

Telnet是一个古老的远程登录协议,可以让本地计算机获得远程计算机的工作能力。它采用了TCP的可靠连接方式,可以连接任何网络互通的远程计算机。不过由于它采用了明文传输方式,存在安全风险,目前已经很少...

Telnet命令是什么?如何使用?(telnet命令在哪里开启)

telnet命令是一个常用的远程登陆工具,使用它,我们可以快捷地登陆远程服务器进行操作。那么如何使用telnet命令呢?首先,我们需要打开telnet功能,任何电脑默认是关闭此功能的,开启方式如下:打...

win11系统如何开启telnet服务(拷贝版本)

  我们要知道,Telnet协议是Internet远程登陆服务的标准协议,可以使用户在本地计算机上完成远程主机的工作,不过对于一些刚接触win11中文版系统的用户来说,可能还不知道telnet服务在哪...

如何开启telnet客户端(如何开启telnet服务)

Telnet协议是TCP/IP协议家族中的一员,是Internet远程登陆服务的标准协议和主要方式,Telnet是常用的远程控制Web服务器的方法。工作中经常用到telnet客户端,但在windows...

Telnet 是什么,如何启用它?(telnet有什么用)

对于Internet等TCP/IP网络,Telnet是一个终端仿真程序。Telnet软件在您的系统上运行并将您的个人计算机链接到网络服务器。它将所有数据转换为纯文本这一事实被认为是易受...

取消回复欢迎 发表评论: