axios

How to abort requests with Axios AbortController?


I'm trying to abort any/all previous Axios requests, using AbortController(): https://axios-http.com/docs/cancellation

PROBLEM: Previous Axios queries do not get aborted; they are fully digested in the front-end.

BACKGROUND:

My app's search experience works as expected, but in the background every request gets fully digested when the user slams away on filters. Instead I want all previous requests to just be cleanly aborted when the user changes the query.

Is the Axios' AbortController intended for this purpose?

UPDATE (WORKS): Thx to @Oluwafemi, my setup is working.

Two things had to be changed:

  1. Set a new instance of AbortController() directly after the abort.
  2. The AxiosSearchController signal needs to be a third parameter going into the Axios function, and not part of the payload (unlike what you see in material online, for whatever reason).

Side note: In addition, not included here is a debouncer wrapping my query function (in my app), which alongside this AbortController, makes for a seemingly good multi-layer management of communication with the API server.

WORKING CODE:

(I redacted a bunch of methods/lines that aren't relevant)

export default class MySearch {

    constructor() {

        // ONE-TIME SETUP

        this.payload = null

        this.active = {
            q: "", // (Query) string e.g. "apples"
            facets: {}, // Objects, each with array of options e.g. { 'size': [ '2 x 2 in', '3 x 3 in' ]}, { 'artists': [ 'mike', 'john', 'jane' ] }
            page: null, // number e.g. 3
            sortBy: null // string, one of: "default" | "newest" | "price_asc" | "price_desc"
        }

        // Declaring this here. Good/bad?
        this.AxiosSearchController = new AbortController()
    }


    async query() {

        return new Promise( async (resolve, reject) => {

            // Abort any previous Axios request
            this.AxiosSearchController.abort()

            // Reinstantiate another instance of AbortController()
            this.AxiosSearchController = new AbortController()

            this.transformURL()

            let requestParams = {
                "page": this.active.page, 
                "sortBy": this.active.sortBy,
                "filter": this.active.facets,
            }

            // Here we tell Axios to associate the request with the controller.
            let AxiosSignal = {
                signal: this.AxiosSearchController.signal
            }

            axios.post('/api/search/' + this.active.q, requestParams, AxiosSignal)
            .then( response => {    
                this.payload = response.data    
                return resolve(response)
            })
            .catch( error => {
                console.error(error)
                return reject(error)
            })

        })
    }

}

Solution

  • Where AxiosSearchController is initialized for MySearch depends on if you want multiple instances of the MySearch to keep the same state of search or to maintain their own state of search.

    When initialized in the constructor, each instance of MySearch has its own state of search like you have in your snippet.

    1. Instance 1 initialized
    2. Instance 2 initialized
    3. Instance 3 initialized
    4. Instance 1 performs request
    5. Instance 2 performs request
    6. Instance 3 performs request
    7. Instance 1 aborts request
    8. Instance 2 continues request till fulfillment
    9. Instance 3 continues request till fulfillment
    

    When initialized outside of the constructor, all instances of MySearch keep the same state of search.

    1. Instance 1 initialized
    2. Instance 2 initialized
    3. Instance 3 initialized
    4. Instance 1 performs request
    5. Instance 2 performs request
    6. Instance 1 has request aborted
    7. Instance 3 performs request
    8. Instance 2 has request aborted
    

    Providing the signal property in the params argument is the proper format to set signal for the request for the axios library.

    However, when aborting any previous request, AxiosSearchController.signal.aborted gets set to true.

    Without resetting this state of the abort controller, you shouldn't be able to make any further requests after the signal is aborted the first time.

    You need to initialize AxiosSearchController after aborting request for the previous search.

    this.AxiosSearchController.abort();
    this.AxiosSearchController = new AbortController();