or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

cancellation.mddata-transformation.mderror-handling.mdheaders.mdhttp-client.mdindex.mdinstance-management.mdinterceptors.md
tile.json

cancellation.mddocs/

Request Cancellation

Cancel HTTP requests to prevent unnecessary network usage, handle component cleanup, and improve application performance. Axios supports both legacy CancelToken and modern AbortSignal approaches.

Capabilities

CancelToken (Legacy)

Token-based cancellation system for backward compatibility.

/**
 * CancelToken constructor
 * @param executor - Function that receives cancel function
 */
class CancelToken {
  constructor(executor: (cancel: Canceler) => void);
  
  /** Promise that resolves when token is canceled */
  promise: Promise<Cancel>;
  /** Cancellation reason if canceled */
  reason?: Cancel;
  
  /** Throw error if request was canceled */
  throwIfRequested(): void;
}

/**
 * Create cancel token source
 * @returns Object with token and cancel function
 */
CancelToken.source(): CancelTokenSource;

interface CancelTokenSource {
  /** Token to use in request config */
  token: CancelToken;
  /** Function to cancel the request */
  cancel: Canceler;
}

interface Canceler {
  (message?: string, config?: AxiosRequestConfig, request?: any): void;
}

Usage Examples:

import axios from "axios";

// Using CancelToken.source()
const source = axios.CancelToken.source();

axios.get("https://api.example.com/data", {
  cancelToken: source.token
}).catch((error) => {
  if (axios.isCancel(error)) {
    console.log("Request canceled:", error.message);
  } else {
    console.error("Request failed:", error);
  }
});

// Cancel the request
source.cancel("Operation canceled by user");

// Using CancelToken constructor
let cancel;
const cancelToken = new axios.CancelToken((c) => {
  cancel = c;
});

axios.post("https://api.example.com/upload", formData, {
  cancelToken: cancelToken,
  onUploadProgress: (progressEvent) => {
    const progress = (progressEvent.loaded / progressEvent.total) * 100;
    console.log(`Upload progress: ${progress}%`);
  }
});

// Cancel upload
cancel("Upload canceled");

AbortSignal (Modern)

Modern browser-standard cancellation using AbortController.

// Uses standard AbortController API
interface AbortController {
  /** Signal to pass to axios config */
  signal: AbortSignal;
  /** Cancel all requests using this signal */
  abort(): void;
}

interface AbortSignal {
  /** Whether the signal has been aborted */
  readonly aborted: boolean;
  /** Event handler for abort events */
  onabort?: ((this: AbortSignal, ev: Event) => any) | null;
  /** Add event listener for abort */
  addEventListener(type: "abort", listener: (ev: Event) => void): void;
  /** Remove event listener for abort */
  removeEventListener(type: "abort", listener: (ev: Event) => void): void;
}

Usage Examples:

// Basic AbortController usage
const controller = new AbortController();

axios.get("https://api.example.com/users", {
  signal: controller.signal
}).catch((error) => {
  if (error.name === "AbortError" || axios.isCancel(error)) {
    console.log("Request aborted");
  } else {
    console.error("Request failed:", error);
  }
});

// Abort the request
controller.abort();

// Timeout with AbortController
function fetchWithTimeout(url, timeout = 5000) {
  const controller = new AbortController();
  
  const timeoutId = setTimeout(() => {
    controller.abort();
  }, timeout);
  
  return axios.get(url, { signal: controller.signal })
    .finally(() => clearTimeout(timeoutId));
}

// Usage
fetchWithTimeout("https://api.example.com/slow-endpoint", 3000)
  .then(response => console.log(response.data))
  .catch(error => {
    if (error.name === "AbortError") {
      console.log("Request timed out");
    }
  });

Cancellation Detection

Check if an error was caused by request cancellation.

/**
 * Check if error is due to request cancellation
 * @param value - Error or any value to check
 * @returns True if value is a cancellation error
 */
axios.isCancel(value: any): boolean;

/**
 * Check if error is an AxiosError (includes cancellation errors)
 * @param payload - Error or any value to check
 * @returns True if payload is an AxiosError
 */
axios.isAxiosError(payload: any): boolean;

Usage Examples:

try {
  const response = await axios.get(url, { cancelToken: source.token });
  console.log(response.data);
} catch (error) {
  if (axios.isCancel(error)) {
    console.log("Request was canceled:", error.message);
  } else if (axios.isAxiosError(error)) {
    console.log("Axios error:", error.message);
    console.log("Status:", error.response?.status);
  } else {
    console.log("Unknown error:", error);
  }
}

// With AbortController
try {
  const response = await axios.get(url, { signal: controller.signal });
  console.log(response.data);
} catch (error) {
  if (error.name === "AbortError" || axios.isCancel(error)) {
    console.log("Request was aborted");
  } else {
    console.log("Other error:", error.message);
  }
}

Cancel and CanceledError

Objects representing cancellation reasons and errors.

/**
 * Cancellation reason object
 */
interface Cancel {
  /** Cancellation message */
  message: string | undefined;
}

/**
 * Error thrown when request is canceled
 */
class CanceledError extends AxiosError {
  constructor(message?: string, config?: InternalAxiosRequestConfig, request?: any);
}

// Legacy alias for backward compatibility
const Cancel = CanceledError;

React Component Integration

Common patterns for canceling requests in React components.

Usage Examples:

import { useEffect, useState } from "react";
import axios from "axios";

// Hook for cancelable requests
function useCancelableRequest() {
  useEffect(() => {
    const controller = new AbortController();
    
    return () => {
      controller.abort(); // Cleanup on unmount
    };
  }, []);
}

// Component with request cancellation
function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(false);
  
  useEffect(() => {
    const controller = new AbortController();
    
    async function fetchUser() {
      setLoading(true);
      try {
        const response = await axios.get(`/api/users/${userId}`, {
          signal: controller.signal
        });
        setUser(response.data);
      } catch (error) {
        if (!axios.isCancel(error)) {
          console.error("Failed to fetch user:", error);
        }
      } finally {
        setLoading(false);
      }
    }
    
    fetchUser();
    
    return () => {
      controller.abort(); // Cancel on cleanup
    };
  }, [userId]);
  
  return loading ? <div>Loading...</div> : <div>{user?.name}</div>;
}

// Custom hook for API calls
function useAPI(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    const controller = new AbortController();
    
    async function fetchData() {
      setLoading(true);
      setError(null);
      
      try {
        const response = await axios.get(url, {
          signal: controller.signal
        });
        setData(response.data);
      } catch (err) {
        if (!axios.isCancel(err)) {
          setError(err);
        }
      } finally {
        setLoading(false);
      }
    }
    
    if (url) {
      fetchData();
    }
    
    return () => {
      controller.abort();
    };
  }, [url]);
  
  return { data, loading, error };
}

Multiple Request Cancellation

Cancel multiple requests with a single signal or token.

Usage Examples:

// Cancel multiple requests with one controller
const controller = new AbortController();

const requests = [
  axios.get("/api/users", { signal: controller.signal }),
  axios.get("/api/posts", { signal: controller.signal }),
  axios.get("/api/comments", { signal: controller.signal })
];

Promise.allSettled(requests)
  .then(results => {
    results.forEach((result, index) => {
      if (result.status === "fulfilled") {
        console.log(`Request ${index} succeeded:`, result.value.data);
      } else if (!axios.isCancel(result.reason)) {
        console.log(`Request ${index} failed:`, result.reason.message);
      }
    });
  });

// Cancel all requests
controller.abort();

// With CancelToken
const source = axios.CancelToken.source();

const cancelableRequests = [
  axios.get("/api/data1", { cancelToken: source.token }),
  axios.get("/api/data2", { cancelToken: source.token }),
  axios.get("/api/data3", { cancelToken: source.token })
];

// Cancel all
source.cancel("Batch operation canceled");

Request Timeout vs Cancellation

Distinguish between timeouts and manual cancellation.

Usage Examples:

// Combine timeout with manual cancellation
function fetchWithTimeoutAndCancel(url, timeout = 5000) {
  const controller = new AbortController();
  let timeoutId;
  
  const request = axios.get(url, { 
    signal: controller.signal,
    timeout: timeout // Axios timeout
  });
  
  // Manual timeout using AbortController
  const timeoutPromise = new Promise((_, reject) => {
    timeoutId = setTimeout(() => {
      controller.abort();
      reject(new Error("Request timed out"));
    }, timeout);
  });
  
  return Promise.race([request, timeoutPromise])
    .finally(() => clearTimeout(timeoutId));
}

// Error handling for different cancellation types
try {
  const response = await axios.get(url, {
    signal: controller.signal,
    timeout: 5000
  });
} catch (error) {
  if (axios.isCancel(error)) {
    console.log("Request was manually canceled");
  } else if (error.code === "ECONNABORTED") {
    console.log("Request timed out");
  } else {
    console.log("Other error:", error.message);
  }
}