How to Properly Cancel HTTP Requests in Your Project

Why is it Necessary to “Cancel” HTTP Requests?

Many front-end developers have experienced this: when quickly typing multiple keywords in a search box, switching pages, or loading content while scrolling, the old requests have not returned, and new requests are being sent out.

The result is:

  • Page display issues (old data overwriting new data)
  • Browsers initiating hundreds or thousands of requests
  • Increased pressure on the backend
  • Decreased user experience

These are typical examples of redundant requests. The significance of “canceling requests” is to bring the browser, network, and logic layer back under control.

Scenario Example: Search Input Triggering Requests

Suppose we implement a search function that triggers a request every time the input changes:

input.addEventListener('input', e => {
  fetch(`/api/search?q=${e.target.value}`)
    .then(res => res.json())
    .then(showResults)
})

If the user types the seven letters of “javascript” continuously, it will initiate seven requests. But we only need the last one. Therefore, we need toβ€” cancel the previous requests.

Method 1: Using <span>AbortController</span> (Modern Browser Standard)

<span>AbortController</span> is a natively supported abort mechanism:

let controller = null

async function search(keyword) {
// Cancel the old request before each call
if (controller) controller.abort()

  controller = new AbortController()
const signal = controller.signal

try {
    const res = await fetch(`/api/search?q=${keyword}`, { signal })
    const data = await res.json()
    showResults(data)
  } catch (err) {
    if (err.name === 'AbortError') {
      console.log('Request canceled:', keyword)
    } else {
      console.error(err)
    }
  }
}

βœ… Advantages:

  • Natively supported, no need for third-party libraries
  • Perfectly integrates with <span>fetch</span>
  • Can be extended to requests for images, streaming, and other resources

Method 2: New Axios Syntax (Based on AbortController)

Starting from Axios <span>v0.22.0</span>, Axios has fully supported the native AbortController, which means we can elegantly cancel requests just like using <span>fetch</span>.

Here is a complete example of a file upload or search request:

import axios from 'axios'

// Create a global controller to cancel requests when needed
let controller = null

async function uploadFile(formData) {
// If there is an ongoing request, cancel it first
if (controller) controller.abort()

// Create a new controller
  controller = new AbortController()

try {
    const response = await axios.post('/api/upload', formData, {
      // Pass the signal to Axios
      signal: controller.signal,
    })

    console.log('Upload successful βœ…', response.data)
  } catch (error) {
    // When the request is canceled, Axios throws an error with message === "canceled"
    if (error.message === 'canceled') {
      console.log('Request has been canceled 🚫')
    } else {
      console.error('Upload failed ❌', error)
    }
  }
}

// Simulate canceling the request
setTimeout(() => {
console.log('User left the page, canceling upload...')
  controller.abort()
}, 2000)

Important Knowledge Points:

  • <span>controller = new AbortController()</span> Each request needs an independent controller instance; otherwise, multiple requests will interfere with each other.
  • <span>signal: controller.signal</span> Axios will listen to this signal, and when <span>controller.abort()</span> is called, it will immediately cancel the request.
  • <span>error.message === 'canceled'</span> Axios’s error message will carry <span>"canceled"</span>, which is used to distinguish between an active cancellation and other errors.

Applicable Scenarios:

  • Cancel requests when switching pages (to avoid React/Vue state update errors)
  • High-frequency triggers (such as search boxes, infinite scrolling)
  • User-initiated interruptions during upload/download tasks

Method 3: Handling in Frameworks (React/Vue)

The most common issue in components is that requests are still ongoing after the component is unmounted. Trying to <span>setState</span> or modify response data at this point will trigger a “memory leak” warning.

🌿 React Hook Scenario:

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

  fetch(`/api/search?q=${query}`, { signal: controller.signal })
    .then(res => res.json())
    .then(setData)
    .catch(err => {
      if (err.name !== 'AbortError') console.error(err)
    })

return () => controller.abort() // Cancel request when component unmounts
}, [query])

This prevents asynchronous updates from executing after the component unmounts, avoiding the “Can’t perform a React state update on an unmounted component” error.

πŸƒ Vue Scenario (Composition API):

const controller = ref(null)

async function fetchData(keyword) {
  controller.value?.abort()
  controller.value = new AbortController()

  const res = await fetch(`/api/search?q=${keyword}`, { signal: controller.value.signal })
  data.value = await res.json()
}

Advanced: Combining Throttle and Debounce

Debounce avoids frequent requests, while Abort prevents wasting old requests.

The combination of both can greatly optimize experiences in search, infinite scrolling, etc.

const debounce = (fn, delay = 500) => {
  let timer
  return (...args) => {
    clearTimeout(timer)
    timer = setTimeout(() => fn(...args), delay)
  }
}

const handleSearch = debounce(keyword => {
  search(keyword)
}, 500)

With the previous cancellation logic, it can be easily implemented:

“Only send one request, always get the latest result.”

Best Practice Summary

Scenario Recommended Solution
Regular fetch requests <span>AbortController</span>
Axios projects <span>AbortController</span>
React/Vue component requests <span>useEffect</span> / <span>onUnmounted</span> + <span>abort()</span>
High-frequency triggers (search box) <span>debounce + abort()</span>
Concurrency control (multiple requests simultaneously) Can be combined with <span>Promise.allSettled</span> / request pool mechanism

Conclusion

Cancelling redundant requests may seem like a small optimization, but it is one of the hallmarks of high-quality front-end development. It reflects a comprehensive control over performance, user experience, and system load.

Leave a Comment