Timeout Handling

Setting request timeouts with AbortController

The Fetch API doesn't have a built-in timeout option. You need to use AbortController combined with setTimeout to implement timeouts.

This is important for user experience — you don't want users waiting indefinitely for a response that may never come.

Basic Timeout Pattern

async function fetchWithTimeout(url, options = {}, timeoutMs = 5000) { const controller = new AbortController(); // Set up the timeout const timeoutId = setTimeout(() => { controller.abort(); }, timeoutMs); try { const response = await fetch(url, { ...options, signal: controller.signal }); clearTimeout(timeoutId); // Request completed, cancel timeout return response; } catch (error) { clearTimeout(timeoutId); if (error.name === 'AbortError') { throw new Error(`Request timed out after ${timeoutMs}ms`); } throw error; } } // Usage try { const response = await fetchWithTimeout('/api/data', {}, 3000); const data = await response.json(); } catch (error) { console.error(error.message); // "Request timed out after 3000ms" }

The AbortController.signal is passed to fetch. When abort() is called, the fetch Promise rejects with an AbortError.

Using AbortSignal.timeout() (Modern)

// Modern approach (Chrome 103+, Firefox 100+, Safari 16+) async function fetchWithTimeout(url, options = {}, timeoutMs = 5000) { try { const response = await fetch(url, { ...options, signal: AbortSignal.timeout(timeoutMs) }); return response; } catch (error) { if (error.name === 'TimeoutError') { throw new Error(`Request timed out after ${timeoutMs}ms`); } if (error.name === 'AbortError') { throw new Error('Request was cancelled'); } throw error; } }

AbortSignal.timeout() is a newer API that handles the timeout automatically. It throws a TimeoutError instead of AbortError.

Combining Timeout with Manual Abort

// Allow both timeout AND manual cancellation function createFetchController(timeoutMs = 5000) { const controller = new AbortController(); const timeoutSignal = AbortSignal.timeout(timeoutMs); // Combine signals: abort if either fires const combinedSignal = AbortSignal.any([ controller.signal, timeoutSignal ]); return { signal: combinedSignal, abort: () => controller.abort() }; } // Usage const { signal, abort } = createFetchController(5000); // Cancel if user navigates away window.addEventListener('beforeunload', abort); const response = await fetch('/api/data', { signal });

Try It Live

ms ms
Set timeout and server delay, then click Fetch. If server delay > timeout, the request will timeout.