در این درس میخوانید
وقتی درخواست شبکه موفق به برقراری ارتباط میشود، هنوز معلوم نیست دادهٔ درست دریافت کردهایم. پاسخ 404، JSON خراب و ساختار غیرمنتظره سه مسئلهٔ متفاوتاند. در درس شانزدهم، بهجای یک API بیرونی، از فایل lessons.json روی وبسرور محلی استفاده میکنیم تا مثال قابل تکرار باشد. در پایان، برنامه باید حالت انتظار، موفقیت، فهرست خالی و خطا را روشن نشان دهد.
دادهٔ نمونه و صفحه را آماده کنید
فایل lessons.json را با محتوای زیر بسازید. JSON از کوتیشن دوتایی استفاده میکند و comment یا ویرگول اضافهٔ انتهای فهرست در آن مجاز نیست. سپس ساختار HTML بعدی را داخل body بگذارید و app.js را با defer بارگذاری کنید. فایلها را از همان وبسرور محلی باز کنید تا درخواست هممبدأ باشد.
[
{ "id": 1, "title": "شروع جاوااسکریپت" },
{ "id": 2, "title": "کار با API" }
]<button id="load" type="button">دریافت درسها</button>
<p id="status" role="status" aria-live="polite"></p>
<ul id="lessons"></ul>
<!-- در head: <script src="./app.js" defer></script> -->fetch برای هر وضعیت HTTP رد نمیشود
fetch برای بسیاری از خطاهای شبکه Promise را رد میکند، اما پاسخ HTTP مانند 404 یا 500 معمولاً هنوز یک Response معتبر است. بنابراین response.ok یا وضعیت را بررسی میکنیم. خواندن body با response.json نیز ناهمگام است و میتواند هنگام تجزیه خطا بدهد. body یک پاسخ را بیدلیل چند بار نخوانید؛ اگر واقعاً چند مصرف لازم است، قرارداد clone و حجم داده را بررسی کنید.
تابع دریافت با مهلت مشخص
async function readLessons(url, timeoutMs = 5000) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeoutMs);
try {
const response = await fetch(url, {
signal: controller.signal,
cache: "no-store"
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const items = await response.json();
if (!Array.isArray(items) || !items.every(item =>
item !== null && typeof item === "object" &&
Number.isInteger(item.id) && typeof item.title === "string"
)) throw new TypeError("Unexpected lesson data");
return items;
} finally {
clearTimeout(timer);
}
}این مثال به محیط مرورگر، پروژه یا Node.js نیاز دارد؛ طبق راهنمای درس اجرا کنید.AbortController راه لغو این درخواست را فراهم میکند. تایمر پس از پایان کار، چه موفق و چه ناموفق، پاک میشود تا لغو دیرهنگام و منابع اضافی باقی نماند. این مهلت در این تابع، خواندن و تجزیهٔ body را هم در بر میگیرد. لغو سمت کاربر الزاماً عملیات سرور را برنمیگرداند؛ برای درخواستهای تغییردهندهٔ داده، این تفاوت مهم است. timeout مرورگر نیز تضمین زمان واقعی سختگیرانه نیست، چون زمانبندی به وضعیت اجرا وابسته است.
رابط را به تابع دریافت وصل کنید
کد بعدی را پس از تابع در app.js قرار دهید. دکمه هنگام انتظار غیرفعال میشود تا ارسال پشتسرهم در همین رابط محدود شود. در موفقیت، Nodeهای واقعی ساخته میشوند؛ در شکست، پیام کاربر نمایش داده میشود و جزئیات فنی برای این نمونهٔ بدون دادهٔ حساس در Console میرود. finally دوباره دکمه را فعال میکند؛ خطای شبکه نباید رابط را برای همیشه در loading نگه دارد.
const button = document.querySelector("#load");
const status = document.querySelector("#status");
const list = document.querySelector("#lessons");
if (!button || !status || !list) throw new Error("Page elements missing");
button.addEventListener("click", async () => {
button.disabled = true;
status.textContent = "در حال دریافت…";
try {
const items = await readLessons("./lessons.json");
const nodes = items.map(item => {
const li = document.createElement("li");
li.textContent = item.title;
return li;
});
list.replaceChildren(...nodes);
status.textContent = items.length ? "درسها آمادهاند" : "هنوز درسی ثبت نشده است";
} catch (error) {
list.replaceChildren();
status.textContent = error instanceof DOMException && error.name === "AbortError"
? "مهلت دریافت تمام شد؛ دوباره تلاش کنید"
: "دریافت درسها انجام نشد";
console.error(error);
} finally {
button.disabled = false;
}
});این مثال به محیط مرورگر، پروژه یا Node.js نیاز دارد؛ طبق راهنمای درس اجرا کنید.چهار حالت را واقعاً آزمایش کنید
- موفقیت: JSON صحیح باید دو عنوان را نمایش دهد.
- فهرست خالی: محتوای JSON را [] کنید؛ پیام بدون داده باید دیده شود.
- 404: نام فایل در fetch را موقتاً اشتباه کنید؛ دریافت باید به مسیر خطا برود.
- دادهٔ نامعتبر: یک title را عدد کنید یا ساختار JSON را خراب کنید؛ تابع باید آن را رد کند.
برای تمرین پایان مهلت، در DevTools شبکه را کند کنید و مقدار timeout را کوچکتر بگذارید؛ نتیجه به کش و زمانبندی واقعی وابسته است. پس از آزمایش، نام فایل و دادهٔ صحیح را برگردانید. این تمرین را با قطعکردن اینترنت برای فایل محلی یکسان ندانید؛ وبسرور محلی ممکن است بدون اینترنت نیز پاسخ دهد. ابزار Network نشان میدهد درخواست به کجا رفته و چه وضعیتی برگشته است.
CORS را با no-cors حل نمیکنیم
اگر داده از یک مبدأ دیگر بیاید، سیاست CORS سرور و نوع درخواست اهمیت دارد. mode برابر no-cors پاسخ را برای استفادهٔ عادی JavaScript قابل خواندن نمیکند و راهحل عمومی خطای CORS نیست. بهجای تلاش برای دورزدن تنظیمات مرورگر، API باید دسترسی مناسب بدهد یا درخواست از مسیر سرورِ مجاز برنامه انجام شود. کلید خصوصی API را در فایل JavaScript عمومی قرار ندهید؛ کاربر میتواند آن را ببیند.
تمرین و پاسخ راهنما
به دادهٔ هر درس یک ویژگی minutes عددی اضافه کنید و هنگام اعتبارسنجی، عدد متناهیِ غیرمنفی را الزامی کنید. سپس در li، عنوان و زمان را به شکل «شروع جاوااسکریپت — ۲۰ دقیقه» نشان دهید. پاسخ بخش اعتبارسنجی زیر باید داخل شرط every اضافه شود؛ برای نمایش فارسی میتوانید از Intl.NumberFormat درس چهارم استفاده کنید. دو ورودی minutes برابر -1 و رشتهٔ «20» باید رد شوند.
// به شرط داخل every اضافه کنید:
Number.isFinite(item.minutes) && item.minutes >= 0
// و داخل ساخت li، به جای متن فعلی بنویسید:
// li.textContent = `${item.title} — ${new Intl.NumberFormat("fa-IR").format(item.minutes)} دقیقه`;منابع انگلیسی
- MDN — Using the Fetch API
- https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
- MDN — AbortController
- https://developer.mozilla.org/en-US/docs/Web/API/AbortController
- MDN — Cross-Origin Resource Sharing
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS
این نمونهٔ دریافت داده برای مجموعهٔ درسآوند طراحی شده است. در درس بعد، JSON و نگهداری داده در مرورگر را همراه با محدودیتها و بازیابی دادهٔ خراب بررسی میکنیم.
پیشنیاز و محیط اجرای مثالها
درس 16 از ۲۰؛ پیشنیاز: درسهای قبلی مجموعه. سه فایل index.html، app.js و lessons.json در یک پوشه بسازید و با وبسرور محلی درس ۱۲ باز کنید؛ از file:// استفاده نکنید. این نمونه برای مرورگر است.
پیشرفت روی همین مرورگر ذخیره میشود.