// 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 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);
})();
// 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);
// 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')
]);
// 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 →