Updated June 2026. Tested on Axios 1.x. Part of the Techalyst JavaScript series.

Once you have a configured Axios instance, the next level is about responsiveness and resilience: cancelling requests that no longer matter, running independent calls in parallel, caching what does not change, and behaving sensibly offline. These four techniques separate a janky app from a smooth one.

Cancelling with AbortController

The web-standard way to cancel a request is AbortController. Pass its signal to Axios, and call abort() to cancel. This is essential for search-as-you-type, where each keystroke should cancel the previous, still-in-flight request so results never arrive out of order.

let controller = null

async function search(query) {
  controller?.abort()              // cancel the previous request
  controller = new AbortController()

  try {
    const results = await api.get('/search', {
      params: { q: query },
      signal: controller.signal,
    })
    return results
  } catch (error) {
    if (error.name === 'CanceledError') return // cancelled, not a real failure
    throw error
  }
}

The key detail is that a cancellation surfaces as a CanceledError, which you should swallow rather than treat as an error. The old CancelToken API still exists but is deprecated, use AbortController in any new code. Pair this with a debounce and your search box is both quiet and race-free.

Running requests concurrently

When several requests do not depend on each other, fire them together instead of awaiting one after another. This is the same promise combinator story applied to HTTP.

// all must succeed; fails fast if any one does
const [stats, activity, notifications] = await Promise.all([
  api.get('/dashboard/stats'),
  api.get('/dashboard/activity'),
  api.get('/notifications'),
])

Use Promise.all when the page genuinely needs everything. When some requests are optional and the page should still render if they fail, use Promise.allSettled and degrade gracefully.

const [usersR, configR] = await Promise.allSettled([
  api.get('/users'),
  api.get('/config'),
])

const users = usersR.status === 'fulfilled' ? usersR.value : []
const config = configR.status === 'fulfilled' ? configR.value : defaultConfig

A related habit: cancel all of a page's in-flight requests when the user navigates away, so stale responses never land in a component that no longer exists. Collect the controllers and abort them on cleanup (in Vue, an onUnmounted hook).

Caching GET responses

Some data barely changes, refetching it on every visit wastes time and bandwidth. The axios-cache-interceptor library wraps your instance and serves cached GET responses transparently.

import { setupCache } from 'axios-cache-interceptor'

const api = setupCache(
  axios.create({ baseURL: import.meta.env.VITE_API_URL }),
  { ttl: 1000 * 60 * 5 }, // cache for 5 minutes
)

After that, repeated api.get('/categories') calls within the TTL hit the cache instead of the network, no change to your calling code. By default it caches in memory, but you can supply custom storage (localStorage, IndexedDB) so the cache survives reloads, and override the cache key when you need per-params granularity.

Behaving well offline

When the connection drops, you want to stop firing requests that are doomed to fail and tell the user what happened, rather than silently piling up errors. Track the network state and reject requests early in a request interceptor.

import { ref } from 'vue'

const isOnline = ref(navigator.onLine)
window.addEventListener('online', () => (isOnline.value = true))
window.addEventListener('offline', () => (isOnline.value = false))

api.interceptors.request.use((config) => {
  if (!isOnline.value) {
    return Promise.reject({ isOfflineError: true, message: 'No internet connection.' })
  }
  return config
})

By rejecting with a custom isOfflineError sentinel, your error handler can tell "the user is offline" apart from "the server returned an error" and show a different message.

try {
  await api.get('/dashboard')
} catch (error) {
  if (error.isOfflineError) return showOfflineToast()
  throw error
}

Watch the isOnline ref at the app root to show a persistent "you are offline" banner that clears when the connection returns. (On native wrappers like Capacitor, prefer the platform's network plugin over navigator.onLine, which is unreliable inside a WebView.)

Wrapping up

Cancel stale requests with AbortController (and swallow the CanceledError), especially for search-as-you-type. Run independent calls together with Promise.all, or Promise.allSettled when you want to degrade gracefully. Cache rarely-changing GET responses with axios-cache-interceptor to cut redundant traffic. And track network status so you can reject requests cleanly when offline and tell the user why. Together these make an app that feels fast and holds up under flaky conditions.

More in the series: Axios instances, interceptors and error handling and JavaScript promises and async/await. Questions welcome below.