专栏首页Node.js开发如何取消ajax请求的回调

如何取消ajax请求的回调

我们在开发过程中有时候会碰到这样的需求,连续发送多个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、获取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属性,代码如下:

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函数中取消所有订阅的任务和异步任务,如何做呢,代码如下:

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组件,代码如何写呢,如下:

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请求的回调,并没有说明其如何实现的,下篇文章咱们通过源码看一看这个功能是如何实现的。

本文分享自微信公众号 - nodejs全栈开发(geekclass),作者:挥刀北上

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-06-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 从http规范角度来看xmlhttprequest发送请求

    最近有点怠工,停更好久,今天分享一篇小白文,原生ajax,看标题肯定不同于其他文章的ajax,而是从http规范角度来看xmlhttprequest发送请求。

    挥刀北上
  • 一道关于组合的js算法题目

    有一个数组,如果有3个值:[3,2,6]。交叉组合后返回:3-2,3-6,2-6,3-2-6

    挥刀北上
  • 前端请求token过期时,刷新token的处理

    在前端开发中,我们经常会遇到使用token,token的作用是要验证用户是否处于登录状态,所以要请求一些只有登录状态才能查看的资源的时候,我们需要携带token...

    挥刀北上
  • 列出spring security的所有SecurityFilterChain

    spring-security-config-4.1.4.RELEASE-sources.jar!/org/springframework/security/c...

    codecraft
  • 开放API网关实践(二) —— 重放攻击及防御

    .example_responsive_1 { width: 200px; height: 50px; } @media(min-width: 290px)...

    草堂笺
  • 两道面试题,带你解析Java类加载机制

    在许多Java面试中,我们经常会看到关于Java类加载机制的考察,例如下面这道题:

    陈树义
  • R包limma作差异基因分析

    limma包最初设计用于分析芯片数据,后经扩展也可适用于RNA-seq数据。通过将read count数据转换为log2-counts per million(...

    用户7585161
  • Java之static作用的全方位总结

     引用一位网友的话,说的非常好,如果别人问你static的作用;如果你说静态修饰 类的属性 和 类的方法 别人认为你是合格的;如果是说 可以构成 静态代码块,...

    小勇DW3
  • MSMQ突破4M限制的方法

        在默认情况下msmq 3.0(windows xp ,windows 2003)最大单个消息(Message size)大小4M;(包括正文和全部指定属...

    阿新
  • Java设计模式学习记录-组合模式

    今天要介绍的设计模式是组合模式,组合模式也是结构型设计模式的一种,它主要体现了整体与部分的诶关系,其典型的应用就是树形结构。组合是一组对象,其中的对象可能包含一...

    纪莫

扫码关注云+社区

领取腾讯云代金券