Skip to content

Bits of .NET

Daily micro-tips for C#, SQL, performance, and scalable backend engineering.

  • Asp.Net Core
  • C#
  • SQL
  • JavaScript
  • CSS
  • About
  • ErcanOPAK.com
  • No Access
  • Privacy Policy
Ajax

Ajax: Use AbortController to Cancel Pending Requests

- 22.03.26 - ErcanOPAK

🚫 Cancel Requests Before They Complete

User types fast, sends 10 search requests. All 10 return, UI flickers. AbortController cancels outdated requests.

The Problem

// ❌ Search on every keystroke - race condition!
searchInput.addEventListener('input', async (e) => {
  const results = await fetch(`/api/search?q=${e.target.value}`);
  const data = await results.json();
  displayResults(data);
});

// User types: "javascript"
// Sends: /api/search?q=j
//        /api/search?q=ja
//        /api/search?q=jav
//        /api/search?q=java
//        /api/search?q=javas
//        ... 10 requests

// All 10 responses come back
// Results flicker as each arrives
// Last one might not be newest!

AbortController Solution

// ✅ Cancel previous request on new keystroke
let controller = null;

searchInput.addEventListener('input', async (e) => {
  // Cancel previous request if exists
  if (controller) {
    controller.abort();
  }
  
  // Create new controller
  controller = new AbortController();
  
  try {
    const results = await fetch(`/api/search?q=${e.target.value}`, {
      signal: controller.signal
    });
    const data = await results.json();
    displayResults(data);
  } catch (error) {
    if (error.name === 'AbortError') {
      console.log('Request cancelled');
    } else {
      console.error('Request failed:', error);
    }
  }
});

// Only latest request completes
// Previous ones cancelled immediately

🎯 How It Works

  1. Create controller: new AbortController()
  2. Pass signal to fetch: fetch(url, { signal: controller.signal })
  3. Cancel when needed: controller.abort()
  4. Catch AbortError: Handle cancellation gracefully

React useEffect Example

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  
  useEffect(() => {
    const controller = new AbortController();
    
    async function fetchUser() {
      try {
        const response = await fetch(`/api/users/${userId}`, {
          signal: controller.signal
        });
        const data = await response.json();
        setUser(data);
      } catch (error) {
        if (error.name !== 'AbortError') {
          console.error('Fetch failed:', error);
        }
      }
    }
    
    fetchUser();
    
    // Cleanup: Cancel request if component unmounts
    // or userId changes before request completes
    return () => controller.abort();
  }, [userId]);
  
  return 
{user?.name}
; }

Timeout with AbortController

⏱️ Auto-cancel after timeout

async function fetchWithTimeout(url, timeout = 5000) {
  const controller = new AbortController();
  
  // Auto-abort after timeout
  const timeoutId = setTimeout(() => controller.abort(), timeout);
  
  try {
    const response = await fetch(url, {
      signal: controller.signal
    });
    clearTimeout(timeoutId);
    return await response.json();
  } catch (error) {
    clearTimeout(timeoutId);
    if (error.name === 'AbortError') {
      throw new Error('Request timeout');
    }
    throw error;
  }
}

// Usage
try {
  const data = await fetchWithTimeout('/api/slow', 3000);
} catch (error) {
  console.error('Request timed out or failed');
}

Cancel Multiple Requests

// One controller for multiple requests
const controller = new AbortController();

Promise.all([
  fetch('/api/users', { signal: controller.signal }),
  fetch('/api/posts', { signal: controller.signal }),
  fetch('/api/comments', { signal: controller.signal })
])
  .then(responses => Promise.all(responses.map(r => r.json())))
  .then(([users, posts, comments]) => {
    console.log('All loaded');
  })
  .catch(error => {
    if (error.name === 'AbortError') {
      console.log('All requests cancelled');
    }
  });

// Cancel all 3 requests at once
document.getElementById('cancel').addEventListener('click', () => {
  controller.abort();
});

✅ Use Cases

  • Search autocomplete: Cancel outdated searches
  • React useEffect: Cancel on unmount/dependency change
  • Pagination: Cancel previous page when user clicks next
  • File uploads: User cancels upload
  • Timeouts: Abort slow requests automatically

💡 Important Notes

  • Browser support: All modern browsers (IE11 needs polyfill)
  • Server still processes: AbortController cancels client-side, server may still complete
  • Always catch AbortError: Don’t treat as real error
  • One controller = one abort: Can’t reuse after abort, create new one

“Search was sending 20+ requests per search. Added AbortController. Now only final request completes. Reduced server load 95%. UI stopped flickering. Users love the smooth experience.”

— Frontend Engineer

Related posts:

Ajax: Why fetch() Doesn't 'Fail' on 404s and How to Fix It

AJAX Requests Fail Randomly? CSRF Token Missing

Ajax Requests Succeed but UI Never Updates

Post Views: 4

Post navigation

JavaScript: Use Destructuring to Extract Values Elegantly
Git: Use git stash to Save Work Without Committing

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

April 2026
M T W T F S S
 12345
6789101112
13141516171819
20212223242526
27282930  
« Mar    

Most Viewed Posts

  • Get the User Name and Domain Name from an Email Address in SQL (950)
  • How to add default value for Entity Framework migrations for DateTime and Bool (858)
  • Get the First and Last Word from a String or Sentence in SQL (836)
  • How to select distinct rows in a datatable in C# (805)
  • How to make theater mode the default for Youtube (751)
  • Add Constraint to SQL Table to ensure email contains @ (578)
  • How to enable, disable and check if Service Broker is enabled on a database in SQL Server (564)
  • Average of all values in a column that are not zero in SQL (531)
  • How to use Map Mode for Vertical Scroll Mode in Visual Studio (489)
  • Find numbers with more than two decimal places in SQL (447)

Recent Posts

  • C#: Use Init-Only Setters for Immutable Objects After Construction
  • C#: Use Expression-Bodied Members for Concise Single-Line Methods
  • C#: Enable Nullable Reference Types to Eliminate Null Reference Exceptions
  • C#: Use Record Types for Immutable Data Objects
  • SQL: Use CTEs for Readable Complex Queries
  • SQL: Use Window Functions for Advanced Analytical Queries
  • .NET Core: Use Background Services for Long-Running Tasks
  • .NET Core: Use Minimal APIs for Lightweight HTTP Services
  • Git: Use Cherry-Pick to Apply Specific Commits Across Branches
  • Git: Use Interactive Rebase to Clean Up Commit History Before Merge

Most Viewed Posts

  • Get the User Name and Domain Name from an Email Address in SQL (950)
  • How to add default value for Entity Framework migrations for DateTime and Bool (858)
  • Get the First and Last Word from a String or Sentence in SQL (836)
  • How to select distinct rows in a datatable in C# (805)
  • How to make theater mode the default for Youtube (751)

Recent Posts

  • C#: Use Init-Only Setters for Immutable Objects After Construction
  • C#: Use Expression-Bodied Members for Concise Single-Line Methods
  • C#: Enable Nullable Reference Types to Eliminate Null Reference Exceptions
  • C#: Use Record Types for Immutable Data Objects
  • SQL: Use CTEs for Readable Complex Queries

Social

  • ErcanOPAK.com
  • GoodReads
  • LetterBoxD
  • Linkedin
  • The Blog
  • Twitter
© 2026 Bits of .NET | Built with Xblog Plus free WordPress theme by wpthemespace.com