附上axios中文文档:axios中文文档|axios中文网 | axios

发起请求没什么好说的:常用get、post发起请求

取消请求:

const CancelToken = axios.CancelToken; // 来自axios的一个属性,用于取消请求
let cancel; // 设置一个变量用于保存取消请求的函数,方便后面直接调用

axios.get('/user/12345', {
    // 取消请求配置项
  cancelToken: new CancelToken(function executor(c) {  
    // executor 函数接收一个 cancel 函数作为参数
    // c是用于取消请求的函数
    cancel = c;
  })
});


// 想要取消请求,直接调用该函数即可
cancel();

取消请求,实际上就是在发起请求的过程中配置一个锚点,而发送请求和响应的时间实际上与网络连接的速度有关系,或者其他有关,所以请求并不是一瞬间就完成,所以在这个过程中我们是有机会阻止请求的。这个锚点在发送请求的过程中一直可以被激活,所以当我们用一个按钮事件来绑定这个cancel()取消的回调函数,只要请求没有完成,我们随时都可以通过点击按钮来取消请求。

但是要考虑几个问题

一、当请求完成后,不论请求的结果是成功,还是出现错误,取消请求这个过程都已完成,此时取消请求就显得没有意义了。所以我们应该在请求成功的response回调中将cancel()置为空,在请求错误的catch(error)回调中也将cancel()置为空。

<script>
        axios.defaults.baseURL = 'http://localhost:4000'
        const CancelToken = axios.CancelToken
        let cancel //设置一个变量用于保存取消请求的函数
        function getProducts1() {
            axios({
                method:'get',
                url:'/products1',
                // 取消请求,肯定是要放在发送请求以后,即请求过程中
                cancelToken: new CancelToken(function executor(c) {  //c是用于取消当前请求的函数
                    // 保存取消函数,用于之后可能需要取消当前请求  ---所以此时cancel是一个函数,可以被调用
                    cancel = c
                })
            }).then(response => {
                // 存在这种情况,当请求完成了,不论是成功还是失败,点取消请求就没有意义了
                // 所以请求成功后将cancel赋值为null
                cancel = null
                console.log('请求1成功了',response.data)
            }).catch(error => {
                // 所以请求错误后也要将cancel赋值为null
                cancel = null
                // 当取消请求后,也会进入请求失败的流程,此时的error就不是原始的error了
                console.log('请求1失败了',error.message,error)
            })  
        

  function cancelRequest() {
          cancel()
           
        }
</script>

二、如果没有发起请求,那么取消请求也是没有意义的。因为发起请求后一定会配置cancelToke,所以cancel就会被赋值为一个取消请求的回调函数。因此我们可以在取消请求前判断一下,是否cancel此时已经是一个取消请求的回调函数,如果是,则取消请求可以生效,否则无需取消。

function cancelRequest() {
            // alert('取消请求')
            // 调用cancel函数来取消请求,可携带一个message参数

            // 但是,存在这种情况,当没有发送请求时,cancel这个变量容器还没有被赋值,所以是不能调用的 ---所以这里需要一个判断
            // 判断是否发送了请求,如果发送了请求,则cancel一定是一个函数,若果没发送请求,则cancel是一个变量容器,无法被调用
            if(typeof cancel === 'function'){
                 cancel('强制取消请求')
            }else {
                console.log('没有可取消的请求')
            }
           
        }

三、如果想要实现的是,不需要按钮,当发起一个请求后,接着点击下一个请求,并且想执行的也是下一个请求,要取消上一个请求的发送。此时我们可以将判断cancel()移动到请求发送之前进行判断。判断一下有没有请求发起

function getProducts1() {
            // 在准备发请求之前,取消未完成的请求
            if(typeof cancel === 'function'){
                cancel()
            }
            axios({
                method:'get',
                url:'/products1',
                // 取消请求,肯定是要放在发送请求以后,即请求过程中
                cancelToken: new CancelToken(function executor(c) {  //c是用于取消当前请求的函数
                    // 保存取消函数,用于之后可能需要取消当前请求  ---所以此时cancel是一个函数,可以被调用
                    cancel = c
                })
            }).then(response => {
                // 存在这种情况,当请求完成了,不论是成功还是失败,点取消请求就没有意义了
                // 所以请求成功后将cancel赋值为null
                    cancel = null
                    console.log('请求1成功了',response.data)
            }).catch(error => {
               
                }else {
                // 而正常的请求出错,就不需要,已经请求完成了,就不需要再取消了
                // 所以请求失败后也要将cancel赋值为null
                    cancel = null
                    console.log('请求1失败了',error.message,error)
                }
            })  
        }

四、但这样做有一个新的问题,就是多次点击时,上一个请求并不会一定被取消。问题在于,当我们发起一个请求,再发起第二个请求时,成功判断了cancel(),取消了第一次请求,但此时第一次请求被取消就会进入自己请求错误的回调-catch(error),这是一个异步执行的,而第二次请求过程中cancelToken是同步执行的,但执行完后cancel()又被第一次请求的异步错误回调赋值为空了。

原本异步错误回调里面的赋值为空是为了让请求完成后(无论成功还是失败)让取消请求变的无意义而无需执行请求取消的。

所以相当于第二个请求中cancel()被异步的置空给覆盖了。此时如果我们要再发起下一个请求,由于判断到此时cancel为null,就没办法取消第二个请求了,这样就会产生很大的抖动,影响用户体验。解决办法就是在每一个请求的失败回调里面都添加一个判断,如果这个请求错误是由于cancel()取消导致的,则不用把cancel置为空,但是如果是请求自己的产生的错误,则置为空,即可解决这个问题。新的请求发起就一定会取消前一个未完成的请求。        

catch(error => {
                // 所以当失败的流程的是因为主动请求取消导致的,那么就不要把cancel置空,或者动它,这样就不会覆盖下一个请求的cancel值
                if(axios.isCancel(error)){
                    console.log('请求1取消的错误',error.message)
                }else {
                // 而正常的请求出错,就不需要,已经请求完成了,就不需要再取消了
                // 所以请求失败后也要将cancel赋值为null
                    cancel = null
               
                console.log('请求1失败了',error.message,error)
                }
            })

五、这样做的话每个请求都需要写很多很多相同的代码,此时就可以设置一个请求拦截器,把发起请求就需要做的相同的事儿都交给请求拦截器去做。比如每一个请求发起前都需要判断是否cancel为取消的回调函数,并且每一个请求都需要配置一个cancelToken的属性。

// 创建一个请求拦截器,来处理请求发起前的工作
        axios.interceptors.request.use(
            config => {
                // 在准备发请求之前,取消未完成的请求
                if(typeof cancel === 'function'){
                    cancel()
                }
                // 添加一个cancelToken的配置
                config.cancelToken = new CancelToken(function executor(c) {  //c是用于取消当前请求的函数
                    // 保存取消函数,用于之后可能需要取消当前请求  ---所以此时cancel是一个函数,可以被调用
                    cancel = c
                })
                return config
            },
            error => {
                return Promise.reject(error)
            }
        )

六、值得注意的是,可以使用同一个 cancel token 取消多个请求。