दिन 20: async/await और fetch: APIs के साथ काम
✅ आज आप सीखेंगे
- async functions और await
- try/catch/finally से error handling
- fetch: GET, response.ok, status codes, JSON
- UI में loading, empty और error states
- JSON POST, validation errors, timeouts और parallel requests संभालना
🏢 असली दुनिया में
अब तक products हमारे JavaScript में लिखे थे। असली दुकानें उन्हें server से load करती हैं ताकि मालिक developer के बिना prices बदल सके। इस course के लिए हमने मुफ़्त practice API बनाया है codingeasily.in/api/sweets/ – यह असली JSON लौटाता है, search support करता है, धीमा (?delay=2000) या ख़राब (?fail=1) किया जा सकता है, और orders validate करता है। आज आप professional front-end developer की तरह इससे जुड़ेंगे।
async और await
const wait = (ms, value) => new Promise(r => setTimeout(() => r(value), ms));
const getUser = id => wait(50, { id, name: 'Aarti' });
const getOrders = user => wait(50, [{ id: 1042, total: 1800 }]);
// Same flow as yesterday’s promise chain – but reads top to bottom
async function showLastOrder(userId) {
const user = await getUser(userId); // pause here until the promise settles
const orders = await getOrders(user);
return `${user.name}: last order #${orders[0].id} (₹${orders[0].total})`;
}
showLastOrder(7).then(console.log); // an async function ALWAYS returns a promise
console.log('This line runs first – await only pauses inside the async function');try/catch/finally से errors
const wait = (ms) => new Promise(r => setTimeout(r, ms));
async function payWithUpi(amount) {
await wait(50);
if (amount > 10000) throw new Error('UPI limit exceeded for this demo');
return `₹${amount} paid via UPI ✅`;
}
async function checkout(amount) {
console.log('⏳ Processing…');
try {
const receipt = await payWithUpi(amount);
console.log(receipt);
} catch (err) {
console.log('❌ Payment failed:', err.message);
} finally {
console.log('Spinner hidden');
}
}
(async () => { await checkout(1800); await checkout(25000); })();लगातार दो awaits एक के बाद एक चलते हैं। अगर requests एक-दूसरे पर निर्भर नहीं, तो साथ शुरू करें: const [menu, offers] = await Promise.all([getMenu(), getOffers()]); – अक्सर दोगुना तेज़।
fetch: आपका पहला API call
<button id="load">Load menu from the API</button>
<pre id="out" style="max-height:220px;overflow:auto;background:#111827;color:#a7f3d0;padding:10px;border-radius:8px">Click the button…</pre>
<script>
const API = 'https://codingeasily.in/api/sweets/';
document.querySelector('#load').addEventListener('click', async () => {
const out = document.querySelector('#out');
out.textContent = 'Loading…';
try {
const response = await fetch(API); // 1. send the request
if (!response.ok) throw new Error('HTTP ' + response.status); // 2. check the status!
const products = await response.json(); // 3. read the JSON body
out.textContent = `Status ${response.status} · ${products.length} products\n\n` +
products.map(p => `${p.id}. ${p.name.padEnd(18)} ₹${p.price}/${p.unit} stock ${p.stock}`).join('\n');
} catch (err) {
out.textContent = 'Could not load the menu: ' + err.message;
}
});
</script>| Step | Code | क्यों |
|---|---|---|
| भेजें | const res = await fetch(url) | Headers आने पर resolve |
| जाँचें | if (!res.ok) throw … | fetch 404/500 पर reject नहीं करता – सिर्फ़ network failure पर |
| पढ़ें | await res.json() / res.text() | Body अलग से पढ़ी जाती है, वह भी async |
| संभालें | सब के चारों ओर try/catch | Offline, timeouts, ख़राब JSON, server errors |
| Status | मतलब | User को दिखाएँ |
|---|---|---|
| 200 OK / 201 Created | सफल | Data / “Order placed” |
| 400 Bad Request / 422 | ग़लत input | Field-level errors |
| 401 / 403 | Login नहीं / अनुमति नहीं | Login prompt |
| 404 Not Found | ग़लत URL या item नहीं | “Product not found” |
| 429 Too Many Requests | Rate limited | “रुककर दोबारा कोशिश करें” |
| 500 / 502 / 503 | Server समस्या | “कुछ गड़बड़ हुई, फिर कोशिश करें” + retry button |
Loading, empty, error और success states
<style>
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; margin-top: 10px; }
.card { border: 1px solid #e5e7eb; border-radius: 12px; overflow: hidden; background: #fff; }
.card img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block; background: #f3f4f6; }
.card div { padding: 8px 10px; font-size: .9rem; }
.muted { color: #6b7280; }
.error { background: #fee2e2; color: #7f1d1d; padding: 10px; border-radius: 8px; }
.skeleton { height: 170px; border-radius: 12px; background: linear-gradient(90deg, #eee 25%, #f6f6f6 50%, #eee 75%); background-size: 200% 100%; animation: sh 1.2s linear infinite; }
@keyframes sh { to { background-position: -200% 0; } }
</style>
<input id="q" placeholder="Search (try kaju, box, xyz)" aria-label="Search">
<select id="cat" aria-label="Category"><option value="">All</option><option value="milk">Milk</option><option value="dry">Dry</option><option value="box">Gift boxes</option></select>
<label><input type="checkbox" id="slow"> Slow network</label>
<label><input type="checkbox" id="fail"> Server error</label>
<div id="view" aria-live="polite"></div>
<script>
const API = 'https://codingeasily.in/api/sweets/';
const view = document.querySelector('#view');
function showLoading() {
view.innerHTML = '<div class="grid">' + '<div class="skeleton"></div>'.repeat(4) + '</div>';
}
function showError(message) {
view.innerHTML = '';
const box = document.createElement('div');
box.className = 'error';
box.textContent = '⚠️ ' + message + ' ';
const retry = document.createElement('button');
retry.textContent = 'Try again';
retry.addEventListener('click', load);
box.append(retry);
view.append(box);
}
function showProducts(products) {
if (products.length === 0) { view.innerHTML = '<p class="muted">No sweets match your search 🙁</p>'; return; }
const grid = document.createElement('div');
grid.className = 'grid';
for (const p of products) {
const card = document.createElement('article');
card.className = 'card';
card.innerHTML = '<img alt=""><div><b></b><br><span class="muted"></span></div>';
card.querySelector('img').src = p.image;
card.querySelector('b').textContent = p.name;
card.querySelector('.muted').textContent = p.stock > 0 ? `₹${p.price}/${p.unit}` : 'Sold out';
grid.append(card);
}
view.replaceChildren(grid);
}
async function load() {
const params = new URLSearchParams(); // builds ?q=kaju&category=dry safely
const q = document.querySelector('#q').value.trim();
const cat = document.querySelector('#cat').value;
if (q) params.set('q', q);
if (cat) params.set('category', cat);
if (document.querySelector('#slow').checked) params.set('delay', 1500);
if (document.querySelector('#fail').checked) params.set('fail', 1);
showLoading();
try {
const res = await fetch(`${API}?${params}`);
if (!res.ok) throw new Error(`The server had a problem (HTTP ${res.status}).`);
showProducts(await res.json());
} catch (err) {
showError(err.message.startsWith('The server') ? err.message : 'You seem to be offline.');
}
}
document.querySelectorAll('#q, #cat, #slow, #fail').forEach(el => el.addEventListener('change', load));
document.querySelector('#q').addEventListener('keyup', e => { if (e.key === 'Enter') load(); });
load();
</script>Loading (skeletons), empty (“no results”), error (retry button के साथ) और success। ज़्यादातर beginner apps सिर्फ़ success बनाते हैं – फिर धीमे 4G पर ख़ाली screen दिखाते हैं। Interviewers और users फ़र्क देखते हैं।
Data भेजना: order POST करना
<form id="order">
<input name="name" placeholder="Name" value="Aarti">
<input name="phone" placeholder="Mobile" value="98290">
<button>Place order (2 kg Kaju Katli)</button>
</form>
<pre id="res" style="background:#f8fafc;padding:10px;border-radius:8px"></pre>
<script>
const API = 'https://codingeasily.in/api/sweets/';
document.querySelector('#order').addEventListener('submit', async (e) => {
e.preventDefault();
const btn = e.currentTarget.querySelector('button');
const out = document.querySelector('#res');
const form = Object.fromEntries(new FormData(e.currentTarget));
btn.disabled = true; // prevent double orders
out.textContent = 'Sending…';
try {
const res = await fetch(API, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...form, items: [{ id: 1, qty: 2 }] }),
});
const data = await res.json();
if (res.status === 422) { // server-side validation errors
out.textContent = '⚠️ ' + data.error + '\n' + Object.entries(data.fields).map(([f, m]) => `• ${f}: ${m}`).join('\n');
} else if (!res.ok) {
throw new Error('HTTP ' + res.status);
} else {
out.textContent = `✅ ${data.message}\nOrder ${data.orderId} · total ₹${data.total} (delivery ₹${data.delivery})`;
}
} catch (err) {
out.textContent = '❌ Could not send the order: ' + err.message;
} finally {
btn.disabled = false;
}
});
</script>AbortController से timeouts और cancel
async function fetchJSON(url, { timeout = 8000, ...options } = {}) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeout);
try {
const res = await fetch(url, { ...options, signal: controller.signal });
const data = await res.json().catch(() => null); // body may be empty
if (!res.ok) {
const error = new Error(data?.error || `HTTP ${res.status}`);
error.status = res.status;
error.data = data;
throw error;
}
return data;
} catch (err) {
if (err.name === 'AbortError') throw new Error('The server took too long. Please try again.');
throw err;
} finally {
clearTimeout(timer);
}
}
// usage
const products = await fetchJSON('https://codingeasily.in/api/sweets/?category=dry', { timeout: 5000 });Browsers page को दूसरे domain के responses तभी पढ़ने देते हैं जब वह server Access-Control-Allow-Origin header से अनुमति दे। हमारा practice API यह भेजता है, इसलिए किसी भी page से चलता है। किसी दूसरे API में CORS error दिखे तो उसे server पर ठीक करना होगा (या अपने backend से बुलाएँ) – front-end code में नहीं।
🏅 आज के Job Skills
Practice tasks
🏋️ Practice – ख़ुद करें
Solution देखें
Quick quiz
🧠 Quick Quiz – ख़ुद को जाँचें
Interview corner
💼 Interview Corner
fetch के साथ errors कैसे संभालें?
try/catch में रखें; network errors और aborts reject करते हैं, पर HTTP errors नहीं, इसलिए response.ok/status जाँचकर throw करें; messages के लिए error bodies parse करें; retry के साथ दोस्ताना states दिखाएँ; AbortController से timeouts; developers के लिए details log करें।
CORS क्या है?
Cross-Origin Resource Sharing: browser का security नियम जो page को दूसरे origin के responses पढ़ने से रोकता है जब तक server Access-Control-Allow-* headers से अनुमति न दे। JSON POST जैसी non-simple requests के लिए preflight OPTIONS requests भेजी जाती हैं।
सारांश
- async/await = पठनीय promises; try/catch/finally में रखें।
- fetch → res.ok जाँचें → await res.json()।
- हमेशा loading, empty, error (+ retry) और success states बनाएँ।
- Headers के साथ JSON POST; 422 field errors संभालें; timeouts के लिए AbortController।
Day 21: modules, npm और modern tooling – code को files में बाँटें और असली teams की तरह Vite इस्तेमाल करें।
