how to cancel/abort ajax request in axios
Asked Answered
H

10

135

I use axios for ajax requests and reactJS + flux for render UI. In my app there is third side timeline (reactJS component). Timeline can be managed by mouse's scroll. App sends ajax request for the actual data after any scroll event. Problem that processing of request at server can be more slow than next scroll event. In this case app can have several (2-3 usually) requests that already is deprecated because user scrolls further. it is a problem because every time at receiving of new data timeline begins redraw. (Because it's reactJS + flux) Because of this, the user sees the movement of the timeline back and forth several times. The easiest way to solve this problem, it just abort previous ajax request as in jQuery. For example:

    $(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);
);

How to cancel/abort requests in axios?

Hessian answered 12/7, 2016 at 12:40 Comment(2)
Possible duplicate of reactJS how to stop it listening to ajax requestWhiz
Your link helped to me solve this problem, but I'm still wondering how to cancel a request, instead stop listening to it...Hessian
P
188

Axios does not support canceling requests at the moment. Please see this issue for details.

UPDATE: Cancellation support was added in axios v0.15.

EDIT: The axios cancel token API is based on the withdrawn cancelable promises proposal.

UPDATE 2022: Starting from v0.22.0 Axios supports AbortController to cancel requests in fetch API way:

Example:

const controller = new AbortController();

axios.get('/foo/bar', {
   signal: controller.signal
}).then(function(response) {
   //...
});
// cancel the request
controller.abort()
Probably answered 13/7, 2016 at 13:37 Comment(9)
if you do this, it cant run anymore againBesnard
const cancelTokenSource = axios.CancelToken.source(); axios.get('/user/12345', { cancelToken: cancelTokenSource.token }); cancelTokenSource.cancel();Brine
Directly from the link: "The axios cancel token API is based on the withdrawn cancelable promises proposal." If I understand it correctly, it is based on something that is not supported, and in that case I don't want to use it. Apparently we have the still experimental abortable fetch (see especially the example); I am curious how this evolves. See also Can I use AbortController?Retarded
Note that, when running axios.post it should be in the 3rd argument! Like this: axios.post('/user/12345', {}, { cancelToken: cancelTokenSource.token });Achorn
Cancellation seems to trigger an error though, see @Vijay sadhu answer below for a catching example when cancellation is a normal expected behaviour.Rowles
@ali based as insipired by or based as in implemented with?Noctilucent
@Noctilucent If my interpretation is correct: implemented with.Retarded
@Retarded thanks for clarifying that it is not certain that it is implemented with it. Sounded weird coming from a widely used library to be made on shaking grounds, specially as many apps are using it. Would be a real problem if your interpretation resulted in being true.Noctilucent
The CancelToken method is deprecated from axios v0.22.0 onwards. They recommend using the AbortController method insteadPuppy
B
54

Using useEffect hook:

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
  }
}, [])

Note: For POST request, pass cancelToken as 3rd argument

Axios.post(`endpointURL`, {data}, {
 cancelToken: ourRequest.token, // 2nd step
})
Bloodred answered 4/5, 2020 at 8:55 Comment(3)
how to trigger cancelToken ? Suppose I have click button to sending request how can I stop it from front end ?Rosarosabel
@NushrataraPriya just call the cancel method ourRequest.cancel()South
it will cancel axios request only but not node execution.Rosarosabel
I
31

Typically you want to cancel the previous ajax request and ignore it's coming response, only when a new ajax request of that instance is started, for this purpose, do the following:

Example: getting some comments from 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
        }
    });
}
Iene answered 12/12, 2019 at 7:1 Comment(1)
Nice solution. If anyone is using axios.create to create an instance, cancelToken and isCancel will not be available. You need to add them. github.com/axios/axios/issues/1330#issuecomment-378961682Putout
B
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;

Brister answered 6/3, 2019 at 11:52 Comment(1)
You should never use a module scoped variable within a component. If you have two of them rendered, each one will wipe out the value set by the previous one.Embrangle
T
7

There is really nice package with few examples of usage called axios-cancel. I've found it very helpful. Here is the link: https://www.npmjs.com/package/axios-cancel

Terisateriyaki answered 17/5, 2017 at 16:37 Comment(0)
A
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')
Arezzo answered 25/11, 2019 at 12:12 Comment(0)
L
5

This is how I did it using promises in node. Pollings stop after making the first request.

 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();               
              })
Luciusluck answered 25/7, 2017 at 7:24 Comment(0)
H
5

Using cp-axios wrapper you able to abort your requests with three diffent types of the cancellation API:

1. Promise cancallation API (CPromise):

Live browser example

 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. Using AbortController signal 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. Using a plain 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. Usage in a custom React hook (Live Demo):

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>
  );
}

Update

Axios v0.22.0+ supports AbortController natively:

const controller = new AbortController();

axios.get('/foo/bar', {
   signal: controller.signal
}).then(function(response) {
   //...
});
// cancel the request
controller.abort()
Hoover answered 27/9, 2020 at 12:19 Comment(2)
I would not recommend this method because if you just cancel the promise, how are you certain that the actual request is stopped and not continuing in the background? It's better to stop the axios request within axios with the way it is built. Also, I just think this way is unnecessarily complex.Innovate
No, cp-axios is an axios wrapper that returns a cancellable promise which supports cancellation of internal tasks, in other words, cancellation of the returned promise will result in aborting of the related network request. It's not just a silent promise cancellation. Just check out the live demo posted above to see how the related network request is actually aborting when you abort the promise (see the network tab in your console).Hoover
M
2

Starting from v0.22.0 Axios supports AbortController to cancel requests in fetch API way:

const controller = new AbortController();

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

CancelToken deprecated You can also cancel a request using a CancelToken.

The axios cancel token API is based on the withdrawn cancelable promises proposal.

This API is deprecated since v0.22.0 and shouldn't be used in new projects

You can create a cancel token using the CancelToken.source factory as shown below:

Musick answered 4/3, 2022 at 8:51 Comment(1)
This seems to be the most up-to-date answer according to the docs -> github.com/axios/axios#abortcontrollerThoroughbred
M
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>
  }
}
Musick answered 4/3, 2022 at 11:56 Comment(2)
Please add an explanation to your codeDraconian
Good that is uses AbortController, but this answer isn't using Axios...Thoroughbred

© 2022 - 2024 — McMap. All rights reserved.