در این فصل چه میسازی#
هر چیزی که تا امروز روی صفحههایت بود، از داخل فایلهای خودت میآمد؛ این فصل صفحهات برای اولین بار دست دراز میکند و از اینترنت دادهٔ زنده میگیرد. یک کارتِ آبوهوا میسازی که با یک کلیک، دمای همین لحظه را از یک سرویسِ واقعی میگیرد و نشان میدهد. و نیمهٔ مهمترِ فصل: کاری میکنیم که اگر اینترنت قطع بود، صفحه بهجای شکستن یک پیامِ دوستانه نشان بدهد — صفحهٔ خوب صفحهای است که روزِ بدِ شبکه هم سرِ پا بماند.

آخر این فصل میتوانی:
- با
fetchاز یک API داده بگیری - توضیح بدهی promise چیست و
async/awaitچه میکنند - جوابِ سرور را با
res.okبسنجی و JSON اش را بخوانی - با
try/catchبرای قطعیِ شبکه یک مسیرِ نجاتِ دوستانه بسازی
قبل از شروع#
- از فصل ۴:
addEventListenerو رویدادclick. - از فصل ۳:
querySelectorوtextContent. - از فصل ۲: template literal.
مثل همیشه فقط یک مرورگر لازم داری، یا فقط همین آزمایشگاه.
۱. fetch: درخواستی که کدِ تو میفرستد#
تا حالا مرورگر فقط وقتی درخواست میفرستاد که تو آدرسی را باز میکردی. fetch همان کار را از داخلِ کد میکند: به یک آدرس درخواست بفرست و جواب را بگیر. آدرسهایی که بهجای صفحهٔ HTML دادهٔ خام پس میدهند API نام دارند، و قالبِ رایجِ این داده JSON است — متنی ساختیافته که تبدیل میشود به همان جنس چیزهایی که میشناسی: مقدارها، آرایهها، و شیءهایی که با نقطه میخوانیشان، مثل event که از فصل ۴ با نقطه میخواندی.
یک تفاوتِ بزرگ با همهٔ کدهای قبلیات: جوابِ اینترنت فوری نیست. برای همین fetch بهجای جواب، بلافاصله یک promise میدهد — یک رسید که میگوید «جوابت در راه است». کلمهٔ await یعنی «همینجا بایست تا این رسید به جوابِ واقعی تبدیل شود»، و هر function ی که تویش await مینویسی باید جلویش async داشته باشد.
۲. اولین درخواست — و اولین undefined#
listener دکمه را بنویس؛ به async که قبل از function نشسته دقت کن:
const updateBtn = document.querySelector('#update-btn');
const weatherLine = document.querySelector('#weather-line');
const url = 'https://api.open-meteo.com/v1/forecast?latitude=35.69&longitude=51.39¤t_weather=true';
updateBtn.addEventListener('click', async function () {
const res = await fetch(url);
const data = await res.json();
console.log(data);
});
این آدرس سرویسِ آبوهوای Open-Meteo است — بدونِ ثبتنام و کلید — و آن دو عدد، عرض و طولِ جغرافیایی تهراناند. res جوابِ خام است؛ res.json() متنِ JSON را به دادهٔ قابلِ استفاده تبدیل میکند — و چون این کار هم زمانبر است، خودش هم promise میدهد؛ برای همین await دوم.
✅ چک کن: دکمه را بزن و console را نگاه کن: یک شیءِ داده میبینی که تویش current_weather هست، با دما و سرعتِ باد.
حالا خرابیِ این فصل. await دوم را عمداً بردار — بنویس const data = res.json(); — و دکمه را بزن. در console بهجای داده این را میبینی:
Promise {<pending>}
نه دما، نه خطا — فقط یک قولِ هنوز عملینشده. و اگر از دلش چیزی بخوانی، undefined میگیری: دادهای که هنوز نرسیده.
🔧 اگر کار نکرد: هر جا انتظارِ داده داشتی و
Promiseیاundefinedدیدی، اولین مظنون همیشه یکی است: یکawaitجا افتاده.awaitرا برگردان و دوباره امتحان کن.
Checkpoint. قبل از ادامه، جواب بده: چرا جلوی functionِ این listener کلمهٔ async نشسته؟ (جواب: چون داخلش await نوشتهایم — قانون همین بود: هر functionی که تویش await باشد، جلویش async میخواهد. و await یعنی «بایست تا این promise به جوابِ واقعی برسد».)
۳. نمایش روی صفحه — و چکِ res.ok#
console.log فقط برای چشمِ توست؛ نمایش برای کاربر همان textContent همیشگی است. دادهٔ برگشتی را با نقطه بخوان و با template literal جمله بساز:
const data = await res.json();
const weather = data.current_weather;
weatherLine.textContent = `دمای الان: ${weather.temperature} درجه — باد: ${weather.windspeed} کیلومتر بر ساعت`;
✅ چک کن: با یک کلیک، دمای واقعیِ همین لحظه روی کارت مینشیند.
ولی یک تلهٔ بیصدا اینجاست. آدرس را عمداً خراب کن — forecast را بکن forecas — و دکمه را بزن. fetch خطا نمیدهد! سرور جواب داده، فقط جوابش «چنین چیزی ندارم» است. آنوقت data.current_weather وجود ندارد و console این خطای واقعی را میدهد:
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'temperature')
🔧 اگر کار نکرد: پیام
Cannot read properties of undefinedتقریباً همیشه یعنی «دادهای که فکر میکردی هست، نیست». قبل از خواندن، سالمبودنِ جواب را بپرس:res.okفقط وقتیtrueاست که سرور جوابِ سالم داده باشد.
if (res.ok) {
const data = await res.json();
const weather = data.current_weather;
weatherLine.textContent = `دمای الان: ${weather.temperature} درجه — باد: ${weather.windspeed} کیلومتر بر ساعت`;
} else {
weatherLine.textContent = 'سرور جواب داد، ولی نه جوابی که میخواستیم. بعداً دوباره امتحان کن.';
}
۴. وقتی اینترنت قطع است: try/catch#
جوابِ بد یک حالت بود؛ حالتِ بدتر این است که اصلاً جوابی نیاید — اینترنت قطع باشد یا سرور خاموش. آنوقت خودِ fetch شکست میخورد و کدت وسطِ راه میترکد؛ در console چیزی شبیه TypeError: Failed to fetch میبینی. مهارش با یک جفتکلمهٔ ساده است: try یعنی «اینها را امتحان کن» و catch یعنی «اگر هر کدام شکست، بهجای ترکیدن بیا اینجا». کدِ نهایی با هر دو مسیرِ نجات:
// script.js
const updateBtn = document.querySelector('#update-btn');
const weatherLine = document.querySelector('#weather-line');
const url = 'https://api.open-meteo.com/v1/forecast?latitude=35.69&longitude=51.39¤t_weather=true';
updateBtn.addEventListener('click', async function () {
try {
const res = await fetch(url);
if (res.ok) {
const data = await res.json();
const weather = data.current_weather;
weatherLine.textContent = `دمای الان: ${weather.temperature} درجه — باد: ${weather.windspeed} کیلومتر بر ساعت`;
} else {
weatherLine.textContent = 'سرور جواب داد، ولی نه جوابی که میخواستیم. بعداً دوباره امتحان کن.';
}
} catch (error) {
console.log(error);
weatherLine.textContent = 'دسترسی به اینترنت نیست یا سرور در دسترس نیست — صفحه سرِ جایش است، بعداً دوباره امتحان کن.';
}
});
حالا آزمایشِ نهایی: آدرس را با یک دامنهٔ ناموجود عوض کن — مثلاً https://no-such-server.example — و دکمه را بزن.
✅ چک کن: بهجای صفحهٔ شکسته یا سکوت، پیامِ دوستانهٔ خودت را میبینی — و بقیهٔ کارت، تیتر و متنش، سرِ جایش است. صفحه بدونِ داده هم هنوز یک صفحهٔ سالم و مفید است؛ به این میگویند progressive: داده تزئینِ صفحه است، نه ستونش.

💡 نکته: آدرسها عوض میشوند و سرویسها روزی خاموش میشوند — قول نمیدهیم این API همیشه باشد. چیزی که میماند الگوست:
fetch← چکِres.ok← خواندنِ JSON ← نمایش، و همیشه یکcatchبا پیامِ آبرومند. اگر این سرویس روزی نبود، صفحهات نمیشکند؛ همان پیامِ catch را نشان میدهد. الگو را که داشته باشی، هر API دیگری همین است.
و برای خطاهایی که هنوز نمیشناسی، یک همراه داری:
🤖 با AI: خطای قرمزِ console را کپی کن و به دستیار AI ات بده: «این خطا یعنی چه؟ مشکل از آدرس است، از اینترنت، یا از کدِ من؟» بعد جوابش را در آزمایشگاه محک بزن — اگر گفت آدرس خراب است، آدرسِ سالم را برگردان و ببین واقعاً درست میشود. پیامهای شبکه بهترین تمرینِ «AI هم اشتباه میکند» هستند، چون AI کد و اینترنتِ تو را ندیده و فقط حدس میزند.
کدِ کاملِ این فصل#
script.js را بالا دیدی؛ این هم index.html:
<!-- index.html -->
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>کارت آبوهوا</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<main class="card">
<h1>آبوهوای تهران</h1>
<p>این کارت با یک کلیک، دادهٔ زندهٔ آبوهوا را از اینترنت میگیرد.</p>
<p id="weather-line">هنوز دادهای نگرفتهایم — دکمه را بزن.</p>
<button id="update-btn" type="button">بهروزرسانی آبوهوا</button>
</main>
<script src="script.js"></script>
</body>
</html>
واژههای تازهٔ این فصل#
| کلمه | تلفظ به حروف فارسی | یعنی چه |
|---|---|---|
| fetch | فِچ | فرستادنِ درخواست به یک آدرس، از داخلِ کد |
| API | اِیپیآی | آدرسی که بهجای صفحه، دادهٔ خام پس میدهد |
| JSON | جِیسان | قالبِ متنیِ داده که به مقدار و آرایه و شیء تبدیل میشود |
| promise | پرامیس | رسیدِ جوابی که هنوز نرسیده |
async / await |
اِیسینک / اِوِیت | علامتِ functionِ منتظر / «بایست تا جواب برسد» |
res.ok |
رِس دات اُکِی | آیا جوابِ سرور سالم است؟ |
try / catch |
ترای / کَچ | امتحان کن؛ اگر شکست، مسیرِ نجات |
تمرینها
اول خودت فکر کن یا امتحان کن — بعد اینجا را باز کن.
در فصل بعد#
جعبهابزارت کامل شد: صفحه میسازی، استایل میدهی، رویداد میگیری، از داده فهرست میسازی و حالا از اینترنت داده میگیری. فصل بعد — آخرین فصلِ تاروپود — هیچ چیزِ تازهای یاد نمیگیری؛ همهچیز را کنارِ هم میگذاری: یک صفحهٔ آشنا را برمیداری و با همین ابزارها جانش میدهی.
به آخر این فصل رسیدی!
اگر ساختی و جواب داد، این دکمه مال توست.