前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何取消ajax请求的回调

如何取消ajax请求的回调

作者头像
挥刀北上
发布2020-07-03 16:35:20
4.3K0
发布2020-07-03 16:35:20
举报
文章被收录于专栏:Node.js开发Node.js开发

我们在开发过程中有时候会碰到这样的需求,连续发送多个ajax请求,请求个数大于等于2,后面的ajax请求发送时,如果前面的ajax请求还没有返回,就取消前面ajax请求回调的执行。

在继续后面的内容之前,先同步一个概念,文中所说的取消ajax的请求,指的是取消ajax请求的回调函数,ajax的请求发送后,这个请求我们是阻止不了的,但是可以取消其回调的执行。

举个简单的例子,你泼了一盆水,水已经泼出去了,水离开盆之后是阻止不了的,但是可以阻止泼出去的后果,比方说你泼了产品经理一盆水,赶紧跑就不会被打到。

接下来,看一下原生js如何处理ajax请求的取消,原生js利用的是XMLhttprequest实例的一个叫做abort的方法,看一下官方文档的代码:

代码语言:javascript
复制
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终止请求的用法很简单,代码示例如下:

代码语言:javascript
复制
const axios = require('axios')
// 1、获取CancelToken
var CancelToken = axios.CancelToken;
// 2、生成source
var 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来实现。

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

点击页面的click按钮,ajax请求回调函数的作用是修改当前组件中state的arr属性,代码如下:

代码语言:javascript
复制

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}>Click</button>
            <Link to={'/admin/list/clock'}>首页</Link>
        </div>
    }
}

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

警报的原因是当前页面渲染的组件已经不是发出请求的组件,而异步的回调还试图去修改上一个组件的状态,此时就会发出警告了。

此时的回调中还保存着上一个组件的状态,形成了一个闭包,如何解决呢?警报中已经给出了提示,cancel all subscriptions and asynchronous tasks in then componentWillUnmount method,啥意思呢?就是在componentWillUnmount函数中取消所有订阅的任务和异步任务,如何做呢,代码如下:

代码语言:javascript
复制
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}>Click</button>
            <Link to={'/admin/list/clock'}>首页</Link>
        </div>
    }
}

阅读源码,首先生成source实例,然后将source的token传入请求函数中,最后在组件即将卸载时调用cancel方法。此时再进行上面的操作就不会出现报警提示了。

上面演示的是class组件,如果是function组件,代码如何写呢,如下:

代码语言:javascript
复制
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}>Click</button>
        <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请求的回调,并没有说明其如何实现的,下篇文章咱们通过源码看一看这个功能是如何实现的。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-06-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 nodejs全栈开发 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档