ajax 请求_如何取消ajax请求的回调

2b3d3cc80281dbcc5dd58b6d3446f71f.png 我们在开发过程中有时候会碰到这样的需求,连续发送多个ajax请求,请求个数大于等于2,后面的ajax请求发送时,如果前面的ajax请求还没有返回,就取消前面ajax请求回调的执行。 在继续后面的内容之前,先同步一个概念,文中所说的取消ajax的请求,指的是取消ajax请求的回调函数,ajax的请求发送后,这个请求我们是阻止不了的,但是可以取消其回调的执行。 举个简单的例子,你泼了一盆水,水已经泼出去了,水离开盆之后是阻止不了的,但是可以阻止泼出去的后果,比方说你泼了产品经理一盆水,赶紧跑就不会被打到。 接下来,看一下原生js如何处理ajax请求的取消,原生js利用的是XMLhttprequest实例的一个叫做abort的方法,看一下官方文档的代码:
var xhr = new XMLHttpRequest(),    method = "GET",    url = "/";xhr.onreadystatechange = () => {    if(xhr.readyState===4&&xhr.status===200){        console.log('ok')    }}xhr.open(method, url, true);xhr.send();xhr.abort();
我们在浏览器中调试代码,在调用abort之后,onreadystatechange会被执行,但是满足readystate=4和status=200的情况就不会出现了。 官方文档提到,xhr调用abort之后,readyState 会被重置为0,readyState变化会触发onreadystatechange函数,而readyState已经被重置为0,此时用户定义的回调函数就不会执行了。 我个人感觉不同的浏览器实现机制可能不一样。我们需要了解的是,ajax请求发送后,在回调调用之前,调用abort,这个ajax的回调就不会被执行了。 以上便是原生js如何处理取消ajax请求回调的原理了。 下面看一下在使用axios过程中如何取消ajax的回调,axios终止请求的用法很简单,代码示例如下:
const axios = require('axios')// 1、获取CancelTokenvar CancelToken = axios.CancelToken;// 2、生成sourcevar source = CancelToken.source();console.log(source.token)axios.get('/user/12345', {//get请求在第二个参数    // 3、注入source.token    cancelToken: source.token}).catch(function (thrown) {    console.log(thrown)});axios.post('/user/12345', {//post请求在第三个参数    name: 'new name'}, {    cancelToken: source.token}).catch(e=>{    console.log(e)});// 4、调用source.cancel("原因"),终止注入了source.token的请求source.cancel('不想请求了');
仔细阅读源码,假如我们要取消axios请求的回调,我们需要调用axios.CancelToken.source方法,得到一个source对象,这个对象有两个属性,一个是token,一个是cancel,token传递到需要被取消请求回调的参数中,cancel是一个方法,调用cancel会取消传递了token的ajax请求。 有哪些场景会用到这个功能呢,假如页面中有个一按钮,每次点击按钮,都会发送异步请求,用户手速快,多次点击,就会发送多次请求,如果我们不做限制,连续点击n次那么页面就会发送n次请求,其回调都会执行,我们需要用户点击第n次请求时,前面的请求中未及时返回的请求会被取消掉,这时就会用到abort方法了。 还有就是在React或者Vue项目中,当我们从PageA切换都PageB的时候,由于PageA页面中请求还没有响应,页面已经切换到PageB了,此时需要取消PageA中的请求的回调。凡此种种都可以用abort来实现。

下面来看个案例,案例页面结构如下:

a4101bbd3775e075b61d4eef1410d6d9.png 点击页面的click按钮,ajax请求回调函数的作用是修改当前组件中state的arr属性,代码如下:
class Three extends Component {    constructor(props) {        super(props)        this.state = {            arr: ''        }    }    click = () => {        axios.get("https://cnodejs.org/api/v1/topics").then(data => {            this.setState({                arr: data.data,            })        }).catch(e => {            console.log(e)        })    }    render() {        let {arr} = this.state;        return <div>            <p>{arr.length} p>            <button onClick={this.click}>Clickbutton>            <Link to={'/admin/list/clock'}>首页Link>        div>    }}

点击click按钮,但是在请求未返回时,我们通过导航切换到其他路由,此时浏览器就会出现警报,如图:

4691953d1dc59f872b80a93b57c44b35.png 警报的原因是当前页面渲染的组件已经不是发出请求的组件,而异步的回调还试图去修改上一个组件的状态,此时就会发出警告了。 此时的回调中还保存着上一个组件的状态,形成了一个闭包,如何解决呢?警报中已经给出了提示,cancel all subscriptions and asynchronous tasks in then componentWillUnmount method,啥意思呢?就是在componentWillUnmount函数中取消所有订阅的任务和异步任务,如何做呢,代码如下:
class Two extends Component{    constructor(props){        super(props)        // 1、调用axios.CancelToken.source()方法生成source实例;        var CancelToken = axios.CancelToken;        var source = CancelToken.source();        this.state = {            source,            arr:''        }    }    click=()=> {        let { token } = this.state.source        // 2、将source.token以cancelToken参数形式传入axios的请求中;        axios.get("https://cnodejs.org/api/v1/topics", {           cancelToken: token         }).then(data => {            this.setState({                arr:data,                loading:'加载完成'            })            console.log(data);        }).catch(e => {            console.log(e)        })     }     componentWillUnmount(){        //  3、在组件即将卸载时取消当前组件的所有异步任务         const { cancel } = this.state.source;         cancel("销毁了")     }    render(){        let {a} = this.state        return <div>            <p>{} has clicked  <strong>{a}strong> Times p>            <button onClick={this.click}>Clickbutton>            <Link to={'/admin/list/clock'}>首页Link>        div>    }}
阅读源码,首先生成source实例,然后将source的token传入请求函数中,最后在组件即将卸载时调用cancel方法。此时再进行上面的操作就不会出现报警提示了。 上面演示的是class组件,如果是function组件,代码如何写呢,如下:
const Index = function (){    let [arr,setArr] = useState('');    var CancelToken = axios.CancelToken;    var source = CancelToken.source();    let Click = ()=>{        axios.get("https://cnodejs.org/api/v1/topics", {         cancelToken: source.token         }).then(data => {            setArr(data.data.data);            console.log("2222");        }).catch(e => {            console.log(e)        })    }    useEffect(()=>{        return ()=>{            console.log("quxiaole")            source.cancel()        }    },[])    return <div>        <p>{arr.length} p>        <button onClick={Click}>Clickbutton>        <Link to={'/admin/list/clock'}>首页Link>    div>}
在函数组件中我们做了同样的事情,大家可以自己测试一下。 现在通常不论是class组件还是函数组件,这种用法都不太常见了,现在一般把数据维护在redux之类的状态容器中,使用状态容器维护数据是不会出现warning警报的,因为数据容器将所有数据维护在了全局作用域,组件在销毁重建过程中修改的都是全局状态下的数据,不存在闭包的情况。 文章到此就要结束了,总结一下: 1.首先介绍了原生js是如何取消ajax请求的,本质是通过调用abort函数将readyState重置为0。 2.然后我们介绍了哪些场景会用到取消ajax请求的功能。 3.最后我们用一个React的案例结合axios,演示使用axios如何取消ajax请求。 本篇文章只演示了在使用axios时如何取消ajax请求的回调,并没有说明其如何实现的,下篇文章咱们通过源码看一看这个功能是如何实现的。 如果你有什么意见或者建议欢迎留言。

7dc067981591c4b4e25c89fbee7d0e86.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值