Follow AiTechWorlds on LinkedIn for professional AI content!Follow Now →
22 minLesson 14 of 40
JavaScript for Web

Fetch API & Async JavaScript

Fetch API & Async JavaScript

Modern web apps load data from APIs without page reloads. The Fetch API combined with async/await is the standard way to do this — cleaner than callbacks, more readable than raw Promises.

Basic Fetch

// fetch returns a Promise that resolves to a Response
const response = await fetch("https://api.example.com/users");
const users = await response.json();  // parse JSON body

// Always check response.ok — 404/500 don't reject the Promise!
if (!response.ok) {
    throw new Error(`HTTP error: ${response.status}`);
}

CRUD Operations

// GET
const user = await fetch("/api/users/42").then(r => r.json());

// POST — send JSON
const newUser = await fetch("/api/users", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ name: "Alice", email: "alice@example.com" })
}).then(r => r.json());

// PUT — update
await fetch(`/api/users/42`, {
    method: "PUT",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ name: "Alice B" })
});

// DELETE
await fetch(`/api/users/42`, { method: "DELETE" });

Error Handling Pattern

async function apiFetch(url, options = {}) {
    const response = await fetch(url, {
        headers: { "Content-Type": "application/json" },
        ...options
    });
    
    if (!response.ok) {
        const err = await response.json().catch(() => ({ message: response.statusText }));
        throw Object.assign(new Error(err.message), { status: response.status });
    }
    
    if (response.status === 204) return null;
    return response.json();
}

// Usage
try {
    const users = await apiFetch("/api/users");
    renderUsers(users);
} catch (err) {
    if (err.status === 401) redirectToLogin();
    else showErrorBanner(err.message);
}

Loading States Pattern

async function loadData(url, containerEl) {
    // Show loading
    containerEl.innerHTML = `<div class="spinner"></div>`;
    containerEl.setAttribute("aria-busy", "true");
    
    try {
        const data = await apiFetch(url);
        containerEl.innerHTML = renderContent(data);
    } catch (err) {
        containerEl.innerHTML = `
            <div class="error">
                <p>${err.message}</p>
                <button onclick="loadData('${url}', this.closest('.container'))">
                    Try Again
                </button>
            </div>
        `;
    } finally {
        containerEl.removeAttribute("aria-busy");
    }
}

Parallel Requests

// Sequential — slow (waits for each one)
const user = await apiFetch(`/api/users/${id}`);
const posts = await apiFetch(`/api/users/${id}/posts`);
const followers = await apiFetch(`/api/users/${id}/followers`);
// Total time = time1 + time2 + time3

// Parallel — fast (all at once)
const [user, posts, followers] = await Promise.all([
    apiFetch(`/api/users/${id}`),
    apiFetch(`/api/users/${id}/posts`),
    apiFetch(`/api/users/${id}/followers`)
]);
// Total time = max(time1, time2, time3)

Cancelling Requests (AbortController)

// Cancel previous request when user types (search input)
let searchController = null;

async function search(query) {
    // Cancel any ongoing search
    searchController?.abort();
    searchController = new AbortController();
    
    try {
        const results = await apiFetch(
            `/api/search?q=${encodeURIComponent(query)}`,
            { signal: searchController.signal }
        );
        renderResults(results);
    } catch (err) {
        if (err.name === "AbortError") return;  // expected, ignore
        showError(err.message);
    }
}

searchInput.addEventListener("input", debounce(e => search(e.target.value), 300));

Real Example: Comment Section

async function initComments(postId) {
    const container = document.querySelector(".comments");
    const form = document.querySelector("#comment-form");
    
    // Load comments
    await loadData(`/api/posts/${postId}/comments`, container);
    
    // Add comment
    form.addEventListener("submit", async (e) => {
        e.preventDefault();
        const text = form.querySelector("textarea").value.trim();
        if (!text) return;
        
        const submitBtn = form.querySelector("[type='submit']");
        submitBtn.disabled = true;
        submitBtn.textContent = "Posting...";
        
        try {
            const comment = await apiFetch(`/api/posts/${postId}/comments`, {
                method: "POST",
                body: JSON.stringify({ text })
            });
            
            form.reset();
            // Prepend new comment to list
            container.insertAdjacentHTML("afterbegin", renderComment(comment));
        } catch (err) {
            alert("Failed to post comment: " + err.message);
        } finally {
            submitBtn.disabled = false;
            submitBtn.textContent = "Post Comment";
        }
    });
}

Next lesson: Why React? JSX & Component Thinking — the foundation of modern UI development.

📱

Get this course's notes on Telegram!

Free cheat sheets, summaries & practice exercises

Get Notes Free →
!