ارتباط با سرور در جاوااسکریپت با Fetch API

تقریباً هر برنامه‌ی وب واقعی در جایی نیاز دارد با یک سرور ارتباط برقرار کند: دریافت لیستی از محصولات، ارسال اطلاعات یک فرم، یا بروزرسانی داده‌ای در پایگاه‌داده. Fetch API ابزار استاندارد و مدرن جاوااسکریپت برای انجام این نوع درخواست‌های شبکه‌ای است و جایگزین روش قدیمی‌تر XMLHttpRequest شده که ساختاری پیچیده‌تر و کمتر خوانا داشت.

ساختار پایه‌ی تابع fetch

تابع سراسری fetch یک آدرس می‌گیرد و یک Promise برمی‌گرداند؛ مفهومی که پیش‌تر با آن آشنا شده‌اید و پایه‌ی اصلی کار Fetch API است:

fetch("https://api.example.com/products")
  .then((response) => {
    console.log(response.status); // مثلاً 200
  });

نکته‌ی مهمی که بسیاری از تازه‌کارها با آن اشتباه می‌کنند این است که خود Promise برگشتی از fetch، مستقیماً داده‌ی نهایی را در اختیار نمی‌گذارد؛ بلکه یک شیء Response برمی‌گرداند که حاوی اطلاعاتی درباره‌ی پاسخ سرور است، از جمله وضعیت درخواست و متدهایی برای استخراج بدنه‌ی پاسخ.

استخراج داده از پاسخ سرور

برای دریافت محتوای واقعی پاسخ، باید یکی از متدهای شیء Response فراخوانی شود؛ رایج‌ترین آن‌ها json() است که خودش نیز یک Promise برمی‌گرداند:

دریافت داده با fetch
خروجی کنسول اینجا نمایش داده می‌شود...

زنجیره‌ی then در اینجا دقیقاً به همان دلیلی که در بحث Promise دیدید شکل گرفته: مرحله‌ی اول پاسخ خام سرور را دریافت می‌کند و مرحله‌ی دوم، پس از تبدیل آن به JSON، به داده‌ی نهایی دسترسی پیدا می‌کند.

بررسی موفقیت‌آمیز بودن درخواست

یکی از رفتارهای غافلگیرکننده‌ی fetch این است که برخلاف انتظار، حتی برای پاسخ‌های خطا مثل ۴۰۴ یا ۵۰۰، Promise را reject نمی‌کند؛ بلوک catch فقط برای خطاهای شبکه‌ای مثل قطعی اینترنت فعال می‌شود. به همین دلیل، بررسی ویژگی ok یا status ضروری است:

fetch("https://api.example.com/products/999")
  .then((response) => {
    if (!response.ok) {
      throw new Error(`خطای سرور: ${response.status}`);
    }
    return response.json();
  })
  .then((data) => console.log(data))
  .catch((error) => console.log(error.message));

ویژگی response.ok برابر true است اگر کد وضعیت پاسخ در بازه‌ی ۲۰۰ تا ۲۹۹ باشد؛ در غیر این صورت باید صراحتاً یک خطا throw شود تا زنجیره‌ی catch آن را دریافت کند.

استفاده از async/await برای خوانایی بیشتر

همان کد بالا با async/await، که در فصل قبل بررسی شد، خواناتر و شبیه کد همزمان (synchronous) نوشته می‌شود:

async function getProduct(id) {
  try {
    const response = await fetch(`https://api.example.com/products/${id}`);

    if (!response.ok) {
      throw new Error(`خطای سرور: ${response.status}`);
    }

    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.log("مشکلی پیش آمد:", error.message);
  }
}

getProduct(5);

جدول متدهای رایج شیء Response

متد نوع داده‌ی خروجی کاربرد رایج
response.json() شیء یا آرایه‌ی جاوااسکریپتی API هایی با پاسخ JSON
response.text() رشته‌ی متنی خام دریافت HTML یا متن ساده
response.blob() داده‌ی باینری دریافت فایل، تصویر یا ویدیو

نکات کلیدی

  • fetch یک Promise برمی‌گرداند که به یک شیء Response تبدیل می‌شود، نه مستقیماً به داده‌ی نهایی.
  • برای استخراج داده باید متدی مثل json() فراخوانی شود که خودش نیز Promise برمی‌گرداند.
  • fetch حتی برای پاسخ‌های خطا مثل 404 نیز Promise را reject نمی‌کند؛ بررسی response.ok الزامی است.
  • ترکیب fetch با async/await خوانایی کد را نسبت به زنجیره‌ی then به‌طور محسوسی بهبود می‌دهد.

جمع‌بندی

Fetch API روشی استاندارد، مبتنی بر Promise و خوانا برای ارسال درخواست‌های شبکه‌ای در جاوااسکریپت ارائه می‌دهد. نکته‌ی حیاتی که باید همیشه در نظر داشت، بررسی صریح وضعیت پاسخ با response.ok است، چون fetch برخلاف تصور رایج، خطاهای HTTP را به‌طور خودکار به بلوک catch نمی‌فرستد. در درس بعدی، نحوه‌ی ارسال داده به سرور با متدهایی مثل POST و تنظیم هدرهای درخواست بررسی خواهد شد.