ajax_fetch_lifecycle
Fetch Request Lifecycle and Error Handling
Section titled “Fetch Request Lifecycle and Error Handling”Understanding Promises
Section titled “Understanding Promises”The fetch API is built on Promises. A Promise represents the eventual result of an asynchronous operation. It can be in one of three states:
- Pending: Initial state, neither fulfilled nor rejected.
- Fulfilled: The operation completed successfully. The promise has a resulting value.
- Rejected: The operation failed. The promise has a reason (an error).
You interact with promises using .then() for fulfilled cases, .catch() for rejected cases, and optionally .finally() for code that runs regardless of success or failure.
`fetch('/api/data')` `.then(response => { // Fulfilled: Network request succeeded (even if it's a 404!)` `console.log('Fetch completed, status:', response.status);` `if (!response.ok) { throw new Error('Bad response'); }` `return response.json(); // Returns a new promise` `})` `.then(data => { // Fulfilled: JSON parsing succeeded` `console.log('Data:', data);` `})` `.catch(error => { // Rejected: Network error OR error thrown in .then()` `console.error('Something went wrong:', error);` `})` `.finally(() => { // Optional: Runs after fulfilled or rejected` `console.log('Fetch attempt finished.');` `});`Using Async/Await
Section titled “Using Async/Await”async and await provide syntactic sugar over promises, making asynchronous code look and behave more like synchronous code, which many find easier to read and write.
async function: Declares a function that implicitly returns a promise. Allows the use ofawaitinside it.await: Pauses the execution of theasyncfunction until the awaited promise settles (either fulfills or rejects). If fulfilled, it returns the resolved value. If rejected, it throws the rejection reason (error).
Error handling with async/await is typically done using standard try...catch...finally blocks.
async function fetchDataWithAsyncAwait(url) { console.log('Starting fetch...'); let data = null; try { // Await pauses execution until fetch promise settles const response = await fetch(url); console.log('Response received, checking status...');
// Check HTTP status (fetch promise fulfills even for 4xx/5xx) if (!response.ok) { // Throw an error to be caught by the catch block throw new Error(`HTTP Error: ${response.status} ${response.statusText}`); }
console.log('Response OK, parsing JSON...'); // Await pauses execution until response.json() promise settles data = await response.json(); console.log('Data parsed successfully:', data); // Use the data here document.getElementById('output').textContent = JSON.stringify(data, null, 2);
} catch (error) { // Catches network errors (fetch rejected) or errors thrown manually (like !response.ok) console.error('Error during fetch operation:', error); document.getElementById('output').textContent = `Error: ${error.message}`; } finally { // Runs whether try succeeded or failed console.log('Fetch attempt finished.'); } return data; // The async function returns a promise resolving with this value (or undefined if error)}
// Example usage// fetchDataWithAsyncAwait('https://jsonplaceholder.typicode.com/todos/1');// fetchDataWithAsyncAwait('https://jsonplaceholder.typicode.com/invalid-url'); // Example for errorHandling Different Error Types
Section titled “Handling Different Error Types”When making requests, errors can occur at different stages:
- Network Errors: The request couldn’t be completed due to network issues (e.g., DNS lookup failed, no internet connection, CORS violation detected before request sent). The
fetch()promise itself will reject. - HTTP Errors: The server responded, but with an error status code (e.g., 404 Not Found, 500 Internal Server Error, 401 Unauthorized). The
fetch()promise will fulfill, butresponse.okwill befalse. You need to checkresponse.okorresponse.statusand handle this explicitly (often by throwing an error). - Parsing Errors: The server responded with status 200 OK, but the body content is not in the expected format (e.g., trying to parse HTML as JSON with
response.json()). The body-reading method (.json(),.text(), etc.) promise will reject. - Application Errors: The server responded with status 200 OK and valid format (e.g., JSON), but the data indicates a logical error (e.g.,
{ "success": false, "error": "Invalid user ID" }). You need to inspect the response data itself to detect these.
Robust error handling involves anticipating these different failure modes.
async function robustFetch(url) { try { const response = await fetch(url);
// Handle HTTP errors (server responded, but with error status) if (!response.ok) { let errorData; try { // Try to get more details from the error response body (often JSON) errorData = await response.json(); } catch (parseError) { // If parsing fails, use status text errorData = { message: response.statusText }; } throw new Error(`HTTP ${response.status}: ${errorData.message || 'Unknown server error'}`); }
// Handle Parsing errors let data; try { data = await response.json(); } catch (parseError) { throw new Error(`Failed to parse JSON response: ${parseError.message}`); }
// Handle Application-level errors (optional, depends on API design) // if (data.success === false) { // throw new Error(`API Error: ${data.error}`); // }
console.log('Success:', data); return data;
} catch (error) { // Catches Network errors (fetch rejected) and errors thrown above console.error(`Fetch failed: ${error.message}`); // Potentially update UI to show error // throw error; // Re-throw if needed for higher-level handling }}
// robustFetch('/api/resource');// robustFetch('/api/not-found'); // Example for 404// robustFetch('/api/invalid-json'); // Example for parsing errorFor more on Promises: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
For more on async/await: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function