如何在axios中取消/中止ajax请求

134

我的应用程序中使用 axios 来进行Ajax请求,使用 reactJSflux 渲染用户界面。应用程序中有一个第三方时间线(reactJS组件)。可以通过鼠标滚动来管理时间线。每次滚动事件后,应用程序会发送Ajax请求以获取实时数据。问题是服务器处理请求的速度可能比下一个滚动事件更慢。在这种情况下,应用程序可能有几个(通常为2-3个)已过时的请求,因为用户向前滚动了。这是一个问题,因为每次接收到新数据时时间线都会重新绘制(因为它是reactJS + flux),所以用户会看到时间线往返移动多次。解决这个问题最简单的方法就是取消之前的ajax请求,就像在jQuery中一样。例如:

    $(document).ready(
    var xhr;

    var fn = function(){
        if(xhr && xhr.readyState != 4){
            xhr.abort();
        }
        xhr = $.ajax({
            url: 'ajax/progress.ftl',
            success: function(data) {
                //do something
            }
        });
    };

    var interval = setInterval(fn, 500);
);

如何在axios中取消/中止请求?


3
可能是 reactJS如何停止监听Ajax请求 的重复问题。 - elreeda
你的链接帮助我解决了这个问题,但我仍然想知道如何取消一个请求,而不是停止监听它... - Rajab Shakirov
10个回答

186

目前Axios不支持取消请求。有关详细信息,请参见此问题

更新:在axios v0.15中添加了取消支持

编辑:axios取消令牌API基于已撤回的可取消Promise提案。

更新2022:从v0.22.0开始,Axios支持AbortController以类似于fetch API的方式取消请求:

示例:

const controller = new AbortController();

axios.get('/foo/bar', {
   signal: controller.signal
}).then(function(response) {
   //...
});
// cancel the request
controller.abort()

8
如果你这样做,它就再也不能运行了。 - KD.S.T.
4
const cancelTokenSource = axios.CancelToken.source(); axios.get('/user/12345', { cancelToken: cancelTokenSource.token }); cancelTokenSource.cancel(); - John Lee
7
请注意,运行 axios.post 时应将其放在第三个参数中!就像这样:axios.post('/user/12345', {}, { cancelToken: cancelTokenSource.token }); - Arno van Oordt
取消似乎会触发一个错误,但是请参见下面@Vijay Sadhu的答案,其中提供了一个捕获示例,当取消是正常预期行为时。 - Eric Burel
5
从axios v0.22.0开始,CancelToken方法已被弃用。他们建议使用AbortController方法代替。 - Spikatrix
显示剩余4条评论

52

使用 useEffect 钩子:

useEffect(() => {
  const ourRequest = Axios.CancelToken.source() // <-- 1st step

  const fetchPost = async () => {
    try {
      const response = await Axios.get(`endpointURL`, {
        cancelToken: ourRequest.token, // <-- 2nd step
      })
      console.log(response.data)
      setPost(response.data)
      setIsLoading(false)
    } catch (err) {
      console.log('There was a problem or request was cancelled.')
    }
  }
  fetchPost()

  return () => {
    ourRequest.cancel() // <-- 3rd step
  }
}, [])

注意:对于POST请求,请将cancelToken作为第3个参数传递

Axios.post(`endpointURL`, {data}, {
 cancelToken: ourRequest.token, // 2nd step
})

如何触发取消令牌?假设我有一个点击按钮发送请求,那么如何从前端停止它呢? - Md Rezaul Karim
@NushrataraPriya 只需调用 ourRequest.cancel() 方法即可取消请求。 - wobsoriano
1
它只会取消axios请求,而不会取消节点执行。 - Md Rezaul Karim

31

通常情况下,您希望取消先前的ajax请求并忽略其响应,仅当该实例的新ajax请求开始时,才执行此操作,请按照以下步骤进行操作:

示例:从API获取一些评论:

// declare an ajax request's cancelToken (globally)
let ajaxRequest = null; 

function getComments() {

    // cancel  previous ajax if exists
    if (ajaxRequest ) {
        ajaxRequest.cancel(); 
    }

    // creates a new token for upcomming ajax (overwrite the previous one)
    ajaxRequest = axios.CancelToken.source();  

    return axios.get('/api/get-comments', { cancelToken: ajaxRequest.token }).then((response) => {
        console.log(response.data)
    }).catch(function(err) {
        if (axios.isCancel(err)) {
           console.log('Previous request canceled, new request is send', err.message);
        } else {
               // handle error
        }
    });
}

不错的解决方案。如果有人正在使用axios.create创建实例,则cancelTokenisCancel将不可用。您需要添加它们。https://github.com/axios/axios/issues/1330#issuecomment-378961682 - Kalimah

27
import React, { Component } from "react";
import axios from "axios";

const CancelToken = axios.CancelToken;

let cancel;

class Abc extends Component {
  componentDidMount() {
    this.Api();
  }

  Api() {
      // Cancel previous request
    if (cancel !== undefined) {
      cancel();
    }
    axios.post(URL, reqBody, {
        cancelToken: new CancelToken(function executor(c) {
          cancel = c;
        }),
      })
      .then((response) => {
        //responce Body
      })
      .catch((error) => {
        if (axios.isCancel(error)) {
          console.log("post Request canceled");
        }
      });
  }

  render() {
    return <h2>cancel Axios Request</h2>;
  }
}

export default Abc;


1
在组件中,您绝对不应该使用模块范围的变量。如果有两个组件被渲染,每个组件都会覆盖前一个组件设置的值。 - Eric Haynes

7

6

https://github.com/axios/axios#cancellation

const CancelToken = axios.CancelToken;
                const source = CancelToken.source();
                let url = 'www.url.com'


                axios.get(url, {
                    progress: false,
                    cancelToken: source.token
                })
                    .then(resp => {

                        alert('done')

                    })

                setTimeout(() => {
                    source.cancel('Operation canceled by the user.');
                },'1000')

5

以下是我在Node中使用Promise完成的方法。在第一次请求后,轮询将停止。

 var axios = require('axios');
    var CancelToken = axios.CancelToken;
    var cancel;
    axios.get('www.url.com',
                      {
                        cancelToken: new CancelToken(
                            function executor(c) {
                                cancel = c;
                             })
                      }
            ).then((response) =>{            
                cancel();               
              })

5
使用 cp-axios 包装器,您可以使用三种不同类型的取消 API 取消请求: 1. Promise 取消 API (CPromise): 实时浏览器示例
 const cpAxios= require('cp-axios');
 const url= 'https://run.mocky.io/v3/753aa609-65ae-4109-8f83-9cfe365290f0?mocky-delay=5s';
 
 const chain = cpAxios(url)
      .timeout(5000)
      .then(response=> {
          console.log(`Done: ${JSON.stringify(response.data)}`)
      }, err => {
          console.warn(`Request failed: ${err}`)
      });
 
 setTimeout(() => {
    chain.cancel();
 }, 500);

2. 使用 AbortController 信号 API:

 const cpAxios= require('cp-axios');
 const CPromise= require('c-promise2');
 const url= 'https://run.mocky.io/v3/753aa609-65ae-4109-8f83-9cfe365290f0?mocky-delay=5s';
 
 const abortController = new CPromise.AbortController();
 const {signal} = abortController;
 
 const chain = cpAxios(url, {signal})
      .timeout(5000)
      .then(response=> {
          console.log(`Done: ${JSON.stringify(response.data)}`)
      }, err => {
          console.warn(`Request failed: ${err}`)
      });
 
 setTimeout(() => {
    abortController.abort();
 }, 500);

3. 使用普通的 axios cancelToken:

 const cpAxios= require('cp-axios');
 const url= 'https://run.mocky.io/v3/753aa609-65ae-4109-8f83-9cfe365290f0?mocky-delay=5s';

 const source = cpAxios.CancelToken.source();
 
 cpAxios(url, {cancelToken: source.token})
      .timeout(5000)
      .then(response=> {
          console.log(`Done: ${JSON.stringify(response.data)}`)
      }, err => {
          console.warn(`Request failed: ${err}`)
      });
 
 setTimeout(() => {
    source.cancel();
 }, 500);

4. 在自定义 React hook 中的使用 (演示):

import React from "react";
import { useAsyncEffect } from "use-async-effect2";
import cpAxios from "cp-axios";

/*
 Note: the related network request will be aborted as well
 Check out your network console
 */

function TestComponent({ url, timeout }) {
  const [cancel, done, result, err] = useAsyncEffect(
    function* () {
      return (yield cpAxios(url).timeout(timeout)).data;
    },
    { states: true, deps: [url] }
  );

  return (
    <div>
      {done ? (err ? err.toString() : JSON.stringify(result)) : "loading..."}
      <button onClick={cancel} disabled={done}>
        Cancel async effect (abort request)
      </button>
    </div>
  );
}

更新

Axios v0.22.0+原生支持AbortController

const controller = new AbortController();

axios.get('/foo/bar', {
   signal: controller.signal
}).then(function(response) {
   //...
});
// cancel the request
controller.abort()

1
我不建议使用这种方法,因为如果你只是取消Promise,那么你怎么确定实际请求已经停止而不是在后台继续执行?最好的方式是使用axios内置的方法来停止请求。此外,我认为这种方式过于复杂,没有必要这样做。 - Zac
不,cp-axios是一个axios包装器,它返回一个可取消的promise,支持取消内部任务,换句话说,取消返回的promise将导致相关网络请求中止。它不仅仅是一个静默的promise取消。只需查看上面发布的实时演示,就可以看到当您中止promise时,相关的网络请求实际上是如何中止的(请参见控制台中的网络选项卡)。 - Dmitriy Mozgovoy

2
从v0.22.0版本开始,Axios支持AbortController以取消请求,类似于fetch API的方式:
const controller = new AbortController();

axios.get('/foo/bar', {
   signal: controller.signal
}).then(function(response) {
   //...
});
// cancel the request
controller.abort()

取消令牌已经被废弃

您也可以使用取消令牌来取消请求。

Axios取消令牌API基于已撤销的可取消Promise提案。

自v0.22.0起,此API已被弃用,不应在新项目中使用。

您可以使用以下示例中显示的CancelToken.source工厂创建取消令牌:


1
根据文档 -> https://github.com/axios/axios#abortcontroller,这似乎是最新的答案。 - vancy-pants

1
import {useState, useEffect} from 'react'

export function useProfileInformation({accessToken}) {
  const [profileInfo, setProfileInfo] = useState(null)

  useEffect(() => {
    const abortController = new AbortController()

    window
      .fetch('https://api.example.com/v1/me', {
        headers: {Authorization: `Bearer ${accessToken}`},
        method: 'GET',
        mode: 'cors',
        signal: abortController.signal,
      })
      .then(res => res.json())
      .then(res => setProfileInfo(res.profileInfo))

    return function cancel() {
      abortController.abort()
    }
  }, [accessToken])

  return profileInfo
}

// src/app.jsx
import React from 'react'
import {useProfileInformation} from './hooks/useProfileInformation'

export function App({accessToken}) {
  try {
    const profileInfo = useProfileInformation({accessToken})

    if (profileInfo) {
      return <h1>Hey, ${profileInfo.name}!</h1>
    } else {
      return <h1>Loading Profile Information</h1>
    }
  } catch (err) {
    return <h1>Failed to load profile. Error: {err.message}</h1>
  }
}

2
请在您的代码中添加一个解释 - Giorgio Tempesta
很好它使用了AbortController,但是这个答案没有使用Axios... - vancy-pants

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接