🏠 Home / Hub

📡 JSON & AJAX Lesson 04 — Async/Await & Error Handling

← Back to JSON Menu  |  🏠 Hub

1. Promise → async/await Evolution

// 1. Callback (old — callback hell)
getData(function(data) {
  getMore(data.id, function(more) {
    save(more, function(result) {
      console.log(result);  // deeply nested!
    });
  });
});

// 2. Promise chain
getData()
  .then(data => getMore(data.id))
  .then(more => save(more))
  .then(result => console.log(result))
  .catch(err => console.error(err));

// 3. async/await (cleanest — looks sync)
async function run() {
  try {
    const data   = await getData();
    const more   = await getMore(data.id);
    const result = await save(more);
    console.log(result);
  } catch (err) {
    console.error(err);
  }
}
async/await = Promise ကို synchronous code လို ဖတ်နိုင်အောင် syntax sugar

2. async/await Rules

// ✅ async function = always returns a Promise
async function greet() {
  return "Hello";
}
greet().then(msg => console.log(msg));  // "Hello"

// await = Promise resolve ကို wait
// await can ONLY be used inside async function
async function fetchData() {
  const res  = await fetch('/api/data');   // waits here
  const data = await res.json();           // waits here too
  return data;                             // Promise wraps this
}

// Top-level await (ES2022, module scripts only)
// <script type="module">
const data = await fetchData();
// </script>

// IIFE pattern (Immediately Invoked)
(async () => {
  const data = await fetchData();
  console.log(data);
})();

3. Error Handling Patterns

// Pattern 1: try/catch (most common)
async function loadUser(id) {
  try {
    const res = await fetch(`/api/users/${id}`);

    if (!res.ok) {
      throw new Error(`HTTP ${res.status}: ${res.statusText}`);
    }

    return await res.json();
  } catch (err) {
    if (err.name === 'TypeError') {
      console.error('Network error — check connection');
    } else {
      console.error('API error:', err.message);
    }
    return null;   // graceful fallback
  } finally {
    hideLoading(); // always runs (cleanup)
  }
}

// Pattern 2: Error wrapper utility
async function safeFetch(url, options) {
  try {
    const res = await fetch(url, options);
    const data = await res.json();
    if (!res.ok) return { data: null, error: data };
    return { data, error: null };
  } catch (err) {
    return { data: null, error: err.message };
  }
}

// Usage — no try/catch needed at call site
const { data, error } = await safeFetch('/api/users');
if (error) showError(error);
else renderUsers(data);

4. Parallel Requests — Promise.all

// Sequential (slow — each waits for previous)
const user  = await fetch('/api/users/1').then(r => r.json());   // wait 200ms
const posts = await fetch('/api/posts?userId=1').then(r => r.json()); // wait 200ms
// Total: ~400ms

// Parallel with Promise.all (fast — all at once)
const [user, posts, comments] = await Promise.all([
  fetch('/api/users/1').then(r => r.json()),
  fetch('/api/posts?userId=1').then(r => r.json()),
  fetch('/api/comments?userId=1').then(r => r.json())
]);
// Total: ~200ms (fastest request determines total time)
// ⚠️ If ANY rejects → all fail

// Promise.allSettled (safe — doesn't fail on one error)
const results = await Promise.allSettled([
  fetch('/api/users/1').then(r => r.json()),
  fetch('/api/BROKEN').then(r => r.json()),    // this fails
  fetch('/api/posts').then(r => r.json())
]);

results.forEach(result => {
  if (result.status === 'fulfilled') console.log(result.value);
  else console.error(result.reason);
});

// Promise.race (first one wins)
const fastest = await Promise.race([
  fetch('https://server1.com/data'),
  fetch('https://server2.com/data')
]);
💡 Independent API calls = Promise.all で parallel — performance huge improvement!

5. Abort Controller — Cancel Request

// Cancel fetch (user navigates away, new request fired, etc.)
let controller = new AbortController();

async function search(query) {
  controller.abort();   // cancel previous request
  controller = new AbortController();

  try {
    const res = await fetch(`/api/search?q=${query}`, {
      signal: controller.signal
    });
    const results = await res.json();
    renderResults(results);
  } catch (err) {
    if (err.name === 'AbortError') {
      console.log('Request cancelled');  // not an error
    } else {
      console.error(err);
    }
  }
}

// Timeout with AbortController
async function fetchWithTimeout(url, ms = 5000) {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), ms);

  try {
    const res = await fetch(url, { signal: controller.signal });
    clearTimeout(timer);
    return res.json();
  } catch (err) {
    if (err.name === 'AbortError') throw new Error('Request timeout');
    throw err;
  }
}

← JSON 03  |  Next: JSON 05 → XHR →

📌 Study Checklist