Fetch API — xatolarni ushlash, loading holati, AbortController

~20 daq · JavaScript · Asinxron JavaScript

Muammodan boshlaylik

Oldingi darsda fetch asoslarini o'rgandik. Lekin haqiqiy ilovada uchta narsa doimo kerak bo'ladi:

Birinchi: Tarmoq uzildi yoki server ishlamayapti — foydalanuvchi nimani ko'rishi kerak? Dastur jim "muzlab" qolmasligi kerak.

Ikkinchi: Server javob berguncha vaqt ketadi. Bu vaqtda foydalanuvchi "sahifa ishlamayapti" deb o'ylamasligi uchun — "yuklanmoqda" belgisi bo'lishi kerak.

Uchinchi: Foydalanuvchi sahifadan chiqib ketdi — so'rov hali yo'lda. Javob kelganda kod "component yo'q" deb xato beradi. Bu so'rovni bekor qilish kerak.

Bu uchala narsa — professional fetch ning majburiy qismlari.


Xatolarni to'g'ri ushlash

Oldingi darsda muhim narsa aytildi: fetch faqat tarmoq uzilganida xato beradi. Server 404 yoki 500 qaytarsa — fetch buni xato deb hisoblamaydi.

Shuning uchun xatolar ikkita turdan kelishi mumkin:

Tarmoq xatosi — internet yo'q, server topilmadi. Bu holda fetch o'zi reject qiladi.

Server xatosi — server ishlayapti, lekin 404, 403, 500 qaytarmoqda. Bu holda javob.ok tekshiriladi.

Hayotiy o'xshatma

Kuryer hujjat eltib borishga jo'nadi. Ikki xil muammo bo'lishi mumkin: birinchi — manzilga umuman yetib bormadi (yo'l yopiq, mashina buzildi) — tarmoq xatosi. Ikkinchi — manzilga yetib bordi, lekin eshikni ochmadi yoki "bunday odam bu yerda yashamaydi" dedi — server xatosi.

Ikkala holat ham "xato", lekin sababi va javobi har xil.

async function xavfsizFetch(url) {
  try {
    const javob = await fetch(url);

    // Server xatosi — tarmoq bo'lsa ham, javob yomon
    if (!javob.ok) {
      throw new Error(`Server xatosi: ${javob.status}`);
    }

    return await javob.json();

  } catch (xato) {
    // Tarmoq xatosi — internet yo'q, server topilmadi
    // Server xatosi — yuqorida throw qilingan

    if (xato.name === "TypeError") {
      // TypeError — ko'pincha tarmoq muammosi
      console.error("Tarmoqda muammo bor:", xato.message);
    } else {
      console.error("Xato:", xato.message);
    }

    return null; // ← xato bo'lsa null qaytaradi, null tekshiriladi
  }
}

const foydalanuvchi = await xavfsizFetch("/api/users/1");
if (!foydalanuvchi) {
  // Sahifada xato xabarini ko'rsat
}

Loading holati — foydalanuvchiga signal

Server javob berguncha vaqt ketadi. Bu vaqtda foydalanuvchi ekranda hech narsa ko'rmaydi. "Ishlamayapti" deb o'ylaydi va sahifani yopadi.

Hayotiy o'xshatma

Restorandagi ofitsiant. Buyurtma berding — ofitsiant oshxonaga ketdi. Uzoq kutding. Agar ofitsiant "taom tayyorlanmoqda, biroz sabr qiling" demagan bo'lsa — siz o'tirib xavotir olasiz: "unutilganmimkin? Buyurtmam kirmadimikan?" Lekin u aytsa — tinch o'tirasiz, chunki jarayon ketayotganini bilasiz.

loading holati — o'sha "tayyorlanmoqda" belgisi.

let yuklanmoqda = false;
let xato        = null;
let malumot     = null;

async function yukla() {
  yuklanmoqda = true;   // ← "tayyorlanmoqda" boshlanadi
  xato        = null;
  render();             // spinner ko'rsatish

  try {
    const javob = await fetch("/api/users");
    if (!javob.ok) throw new Error(`${javob.status}`);
    malumot = await javob.json();
  } catch (err) {
    xato = err.message;
  } finally {
    yuklanmoqda = false; // ← xato bo'lsa ham, bo'lmasa ham to'xtatiladi
    render();
  }
}

function render() {
  if (yuklanmoqda) {
    // spinner ko'rsat
  } else if (xato) {
    // xato xabarini ko'rsat
  } else {
    // malumotni ko'rsat
  }
}

Nima uchun finally da loading = false?

try da xato bo'lsa — finally baribir ishlaydi. Agar yuklanmoqda = false ni faqat try ichiga qo'ysangiz — xato bo'lganda spinner abadiy aylanib turadi. finally — "xato bo'lsa ham, bo'lmasa ham" degan kafolat.


AbortController — so'rovni bekor qilish

Foydalanuvchi mahsulotlar sahifasiga kirdi. fetch jo'nadi — 2 soniya ketadi. Foydalanuvchi fikr o'zgartirib boshqa sahifaga o'tdi.

Eski sahifaning fetch hali yo'lda. 2 soniyadan keyin javob keladi. Kod eski sahifaga ma'lumot yozmoqchi bo'ladi — lekin u sahifa endi yo'q. Xato chiqadi yoki, yanada yomoni, noto'g'ri sahifaga ma'lumot yoziladi.

Hayotiy o'xshatma

Liftni chaqirdingiz. Yuqoriga ketmoqchi edingiz. Lekin fikr o'zgarib, pillapoyadan yurishga qaror qildingiz. Lift hali ketmoqda. "Bekor qilish" tugmasini bossangiz — lift to'xtaydi, boshqa qavatga boradi.

AbortController — o'sha "bekor qilish" tugmasi.

const kontroller = new AbortController();

// So'rovni AbortController bilan bog'lash
const javob = await fetch("/api/malumot", {
  signal: kontroller.signal, // ← "mana boshqaruv tugmasi"
});

// Istalgan payt bekor qilish
kontroller.abort();

kontroller.abort() chaqirilganda — fetch darhol to'xtatiladi va AbortError beradi.

Amalda — sahifadan chiqilganda bekor qilish

function sahifahYuklash() {
  const kontroller = new AbortController();

  async function yukla() {
    try {
      const javob = await fetch("/api/users", {
        signal: kontroller.signal,
      });
      if (!javob.ok) throw new Error(`${javob.status}`);
      const data = await javob.json();
      ro_yxatniKo_rsat(data); // ← sahifa hali bor bo'lsa ishlaydi
    } catch (xato) {
      if (xato.name === "AbortError") {
        // Bu xato emas — biz o'zimiz bekor qildik, e'tibor bermaslik kerak
        return;
      }
      xatoBoshqar(xato);
    }
  }

  yukla();

  // Sahifadan chiqilganda:
  return function tozalash() {
    kontroller.abort(); // ← so'rov yo'lda bo'lsa — bekor qilindi
  };
}

const tozala = sahifahYuklash();
// Foydalanuvchi boshqa sahifaga o'tganda:
tozala();

Nima uchun AbortError alohida tekshiriladi?

Abort — biz o'zimiz qilgan narsa, xato emas. Buni oddiy xato bilan bir xil ko'rsatish noto'g'ri bo'lar edi. Shuning uchun xato.name === "AbortError" bo'lsa — jimgina qaytib ketiladi.


Hammasini birga — professional fetch funksiyasi

async function apidan_ol(url, signal) {
  try {
    const javob = await fetch(url, { signal });

    if (!javob.ok) {
      const xabarMatni = await javob.text();
      throw new Error(`${javob.status}: ${xabarMatni}`);
    }

    return await javob.json();

  } catch (xato) {
    if (xato.name === "AbortError") return null; // bekor qilindi — xato emas
    throw xato;                                  // haqiqiy xato — yuqoriga uzat
  }
}

// Ishlatilinishi:
async function foydalanuvchilarYukla() {
  const kontroller = new AbortController();
  let yuklanmoqda  = true;
  let xato         = null;

  render({ yuklanmoqda, xato, malumot: null });

  try {
    const malumot = await apidan_ol("/api/users", kontroller.signal);
    render({ yuklanmoqda: false, xato: null, malumot });
  } catch (err) {
    render({ yuklanmoqda: false, xato: err.message, malumot: null });
  }

  return () => kontroller.abort(); // ← tozalash funksiyasi
}

Tez-tez uchraydigan xatolar

finally da loading = false unutish:

Xato bo'lganida try bloki o'rtada to'xtaydi. Agar loading = false faqat try oxirida bo'lsa — xatoda spinner abadiy aylanadi.

// ❌
try {
  malumot = await fetch("/api");
  yuklanmoqda = false; // xato bo'lsa bu qatirga yetib kelmaydi
} catch (e) { xato = e; }

// ✅
try {
  malumot = await fetch("/api");
} catch (e) {
  xato = e;
} finally {
  yuklanmoqda = false; // har holda ishlaydi
}

AbortError ni oddiy xato kabi ko'rsatish:

// ❌ — "So'rov bekor qilindi" deb foydalanuvchiga ko'rinadi
catch (xato) {
  xatoXabari = xato.message; // AbortError ham shu yerga tushadi
}

// ✅
catch (xato) {
  if (xato.name === "AbortError") return; // ← jimgina chiqish
  xatoXabari = xato.message;
}

Har bir so'rov uchun alohida kontroller yaratmaslik:

Bitta kontroller bir marta abort qilinsa — qayta ishlatib bo'lmaydi. Har yangi so'rov uchun yangi AbortController yaratish kerak.

// ❌ — ikkinchi so'rov darhol bekor qilingan kontroller bilan
const kontroller = new AbortController();
kontroller.abort();
fetch("/api/users", { signal: kontroller.signal }); // darhol abort

// ✅ — har safar yangi
async function qaytaYukla() {
  const kontroller = new AbortController(); // ← yangi
  await fetch("/api/users", { signal: kontroller.signal });
}

Xulosa

Professional fetch uchta narsadan iborat:

Xato ushlash — javob.ok tekshiriladi, try/catch da ikkala tur xato ushlanadi.

Loading holati — yuklanmoqda = true boshida, finally da yuklanmoqda = false. Foydalanuvchi jarayon ketayotganini biladi.

AbortController — sahifadan chiqilganda so'rov bekor qilinadi. AbortError — xato emas, jimgina qaytish.

Bu uchala narsa birga bo'lsa — fetch ishonchli va professional ishlaydi.


Keyingi dars

43-dars: DOM — getElementById, querySelector, querySelectorAll