async function safeFetch(url, options = {}) { const controller = new AbortController(); const id = setTimeout(() => controller.abort(), options.timeout || 10000); // Default 10s timeout try { const response = await fetch(url, { ...options, signal: controller.signal }); clearTimeout(id); // Clear timeout if fetch completes if (!response.ok) { const errorDetail = await response.json().catch(() => ({})); // Try to parse error message const errorMessage = errorDetail.message || response.statusText || 'An unknown error occurred'; throw new Error(`HTTP error! Status: ${response.status}. ${errorMessage}`); } return await response.json(); } catch (error) { clearTimeout(id); // Ensure timeout is cleared on error too if (error.name === 'AbortError') { console.error('Fetch request timed out or was aborted:', error); throw new Error('Request timed out or was cancelled.'); } else if (error.message.includes('Failed to fetch')) { // Network error (CORS, no internet, etc.) console.error('Network error or CORS issue:', error); throw new Error('Network error. Please check your internet connection or try again later.'); } else { console.error('API request error:', error); throw error; } } } // Example usage: // safeFetch('https://api.example.com/data', { timeout: 5000 }) // .then(data => console.log('Data:', data)) // .catch(error => console.error('Failed to fetch safely:', error.message)); // // // To intentionally abort before timeout: // const controller2 = new AbortController(); // setTimeout(() => controller2.abort(), 100); // safeFetch('https://api.example.com/slow-data', { signal: controller2.signal }) // .catch(error => console.error('Aborted example:', error.message));