IlmHamroh
JavaScript Full-stack/10-qism. JavaScript chuqur58/65-dars22 daqiqa
Mundarija (30)

JavaScript'da xato strategiyasi: qayerda ushlash, nima ko'rsatish va nimani loglash

Qisqacha: Xatolar ikki xil: operatsion (kutilgan — buzuq kirish, band stol, uzilgan tarmoq) va bug (kodning o'z kamchiligi). Operatsion xatoni chegarada — masalan, tugma tinglovchisida — ushlab, foydalanuvchiga "nima bo'ldi va nima qilish kerak" deb ko'rsatasiz. Bug'ni yashirmaysiz: foydalanuvchiga umumiy xabar, dasturchiga to'liq log. Tashqi ma'lumotni kirishdayoq tekshirasiz (fail fast) va holatni faqat hamma tekshiruvdan keyin o'zgartirasiz.

Bu darsda

  • Operatsion xato va bug'ni ajratib, har biriga to'g'ri javob tanlaysiz.
  • try...catch ni qayerga qo'yishni — "chegara" qoidasini — bilasiz.
  • Foydalanuvchi xabarini va dasturchi logini ajratib yozasiz.
  • "Fail fast", himoyaviy dasturlash va "hammasi yoki hech narsa" tamoyillarini qo'llaysiz.
  • throw o'rniga natija obyekti qaytaradigan Result naqshini taniysiz.
  • vazifalar importini yakunlab, feature/import branch'ini main ga qo'shasiz.

Oldin bilishingiz kerak: try / catch / finally, throw va Error obyekti, O'z xato klasslaringiz, Prototype pollution.

1. Nega bu kerak?

Uch darsda asboblarni yig'dik: try/catch/finally, throw, cause, o'z xato klasslari. Lekin asbobning o'zi hali usul emas. Endi savol: ularni qayerda va qanday ishlatish kerak?

Ikki «Bahor» saytini tasavvur qiling. Ikkalasida ham bron formasi bor.

  • Birinchisida har funksiyada try...catch va har catch da return null. Sayt hech qachon "yiqilmaydi". Lekin bir kuni bronlar bazaga yozilmay qoldi — va hech kim bilmadi, chunki hamma xato jimgina yutilgan. Jasur aka buni bir haftadan keyin, mehmonlar shikoyat qilganda bildi.
  • Ikkinchisida try umuman yo'q. Har kichik muammo — oq ekran. Mehmon nima qilishni bilmaydi va telefon qiladi.

Ikkalasi ham yomon. To'g'ri yo'l — to'rt savolga javob: xato kutilganmi? Uni qayerda ushlash kerak? Foydalanuvchiga nima deyiladi? Dasturchi uni qanday bilib oladi? Shu javoblar to'plami — xato strategiyasi.

O'xshatish: kasalxonadagi navbatchi shifokor. U avval bemorlarni ajratadi: shamollashga dori beradi, jiddiy holatni mutaxassisga yuboradi. Bemorga oddiy so'z bilan tushuntiradi, kartaga esa hamma tafsilotni yozadi — keyingi shifokor uchun.

2. Ikki xil xato

2.1 Operatsion xato va bug

Operatsion xato Dasturchi xatosi (bug)
Misol Buzuq JSON, stol band, tarmoq uzildi undefined dan xususiyat o'qish
Kim aybdor Tashqi dunyo Bizning kod
Oldindan bilsa bo'ladimi Ha — albatta sodir bo'ladi Yo'q — bilsak, tuzatardik
Nima qilish Ushlash va javob berish Kodni tuzatish

Operatsion xato (operational error) — to'g'ri yozilgan dastur ham duch keladigan holat. Foydalanuvchi buzuq matn yuboradi, Wi-Fi uziladi, stol band bo'ladi. Ularni yo'q qilib bo'lmaydi — faqat to'g'ri javob berish mumkin.

Dasturchi xatosi (programmer error), ya'ni bug — kodning o'zidagi kamchilik. TypeError: Cannot read properties of undefined — deyarli har doim bug. Unga "javob" yo'q: dastur kutmagan holatga tushgan, davom etsa yangi xatolar chiqadi. Yagona davo — topish va tuzatish.

2.2 Bir xil tur — har xil ma'no

Farq xato turida emas, vaziyatda. Bitta SyntaxError ikki xil bo'lishi mumkin:

  • Foydalanuvchi maydonga yozgan JSON buzuq — operatsion, buni kutish kerak.
  • Dasturchi kodda o'zi yozgan JSON satrida vergulni unutgan — bug.

Shuning uchun savol "qaysi tur?" emas, "kim aybdor va kutilganmi?". Kutilgan xatolarni biz o'z turimizga o'raymiz (O'z xato klasslaringiz darsidagi BahorXatosi kabi). Shunda qolgan hamma narsa o'z-o'zidan "kutilmagan", ya'ni bug bo'lib qoladi.

Tekshirib ko'ring: Uch holat: (1) mehmon telefon raqamini harf bilan yozdi; (2) render() ichida holat.vazifalar.map o'rniga holat.vazifalar.mapp yozilgan; (3) server javob bermadi. Qaysi biri bug?

Javob

Faqat (2). (1) — foydalanuvchi kiritishi, (3) — tarmoq: ikkalasi to'g'ri kodda ham sodir bo'ladi, demak operatsion. (2) — kod yozilganda xato qilingan; uni hech qanday catch "hal qila" olmaydi, faqat kodni tuzatish kerak.

3. Qayerda ushlash: chegara qoidasi

3.1 Uchta haqiqiy sabab

try / catch darsida ko'rdik: xato chaqiruvlar steki bo'ylab o'zi yuqoriga ko'tariladi. Demak try ni har funksiyaga qo'yish shart emas. catch yozishdan oldin so'rang: men bu xato bilan nima qila olaman? Yaxshi javob uchta:

  1. Hal qilish. Zaxira qiymat berish yoki qayta urinish. Masalan, saqlangan sozlama buzuq bo'lsa — standart sozlamaga qaytish.
  2. Ko'rsatish. Foydalanuvchiga xabar berish — buni faqat ekran bilan bog'liq joy qila oladi.
  3. Ma'no qo'shib qayta tashlash. Past darajadagi xatoni o'z turingizga cause bilan o'rash (throw va Error).

Uchalasidan birortasini qila olmasangiz — catch yozmang. Xato yuqoriga, buni qila oladigan joyga ketsin.

3.2 Chegara

"Ko'rsatish" joyining nomi — chegara (boundary): dastur tashqi dunyo bilan uchrashadigan nuqta. Xuddi bojxona kabi: tashqaridan kelgan hamma narsa shu yerda tekshiriladi. Brauzerda chegara — hodisa tinglovchisi, serverda — har so'rov ishlovchisi, konsol dasturida — asosiy funksiya. Ichkaridagi funksiyalar toza qoladi: faqat throw qiladi, ushlamaydi.

Diagrammada — bizning vazifalar importi. O'qlarni yuqoridan pastga, keyin pastdan yuqoriga kuzating:

flowchart TD
  T["Tugma tinglovchisi — chegara"] --> P["paketniOqi — throw"]
  P --> J["JSON.parse — SyntaxError"]
  J -- "o'raladi: VazifaXatosi + cause" --> P
  P -- "ko'tariladi" --> T
  T -- "VazifaXatosi" --> U["Foydalanuvchiga xabar"]
  T -- "boshqa xato" --> L["Umumiy xabar va qayta tashlash"]
  • Ichki qavat (JSON.parse) — o'z xatosini tashlaydi.
  • O'rta qavat (paketniOqi) — kutilgan xatoni ma'noli turga o'raydi. U ekran haqida hech narsa bilmaydi.
  • Chegara (tinglovchi) — yagona try...catch: o'zimiznikini ko'rsatadi, begonani qayta tashlaydi.

Qayta tashlangan bug konsolda Uncaught bo'lib chiqadi. Hech kim ushlamagan xatolar uchun "oxirgi himoya chizig'i" ham bor — u Ushlanmagan xatolar va global xato ushlash darsida (11-qismda, hozir bilish shart emas).

Ba'zan chegara sikl ichida bo'ladi. Kun oxirida 50 ta bron qayta ishlanadi, bittasi buzuq — qolgan 49 tasi to'xtamasligi kerak. Shunda try sikl ichida turadi, xuddi try / catch darsidagi "Kunlik hisob" mashqidagidek: har element — alohida ish, har ishning o'z chegarasi.

Tekshirib ko'ring: narxniOl(taom) funksiyasi menyuda yo'q taomda TopilmadiXatosi tashlaydi. Uni narxniOl ning o'zida ushlab, 0 qaytarish to'g'rimi?

Javob

Odatda yo'q. 0 — "tekin taom" degan yolg'on javob: chek noto'g'ri hisoblanadi va hech kim bilmaydi. narxniOl bu xato bilan to'g'ri ish qila olmaydi — na ko'rsata oladi, na to'g'ri zaxira qiymati bor. Xato chekni chizadigan joygacha ko'tarilsin, u yerda "Taom menyuda yo'q" deb ko'rsatiladi.

4. Foydalanuvchiga nima ko'rsatish

Yaxshi xato xabari ikki savolga javob beradi: nima bo'ldi va endi nima qilish kerak. Foydalanuvchi tilida, ichki tafsilotsiz:

Yomon Yaxshi
SyntaxError: Expected ',' or '}'… Matn JSON emas. Eksportni to'liq oling.
Xato yuz berdi Bron 10:00–22:00 da qabul qilinadi. Boshqa vaqtni tanlang.
TypeError: Cannot read properties of null Kutilmagan xato: import bajarilmadi. Sahifani yangilang.
Error 0x80070057 Odam soni 1 dan 20 gacha bo'lsin.

Uchinchi qatorga qarang. Bug'da ham foydalanuvchiga nimadir deyiladi — aks holda u tugma ishlamadi deb o'ylab, qayta-qayta bosadi. Lekin ichki matn emas, umumiy xabar.

Nega stack va ichki matn ko'rsatilmaydi? Birinchidan, mehmon at paketniOqi (vazifalar.js:214:11) dan hech narsa tushunmaydi. Ikkinchidan — xavfsizlik: xato matnida fayl yo'llari, funksiya nomlari, serverda esa jadval nomlari bor. Hujumchi ulardan tizimni qayerdan buzishni bilib oladi. Shuning uchun mijozga faqat tayyor xabar boradi, tafsilot logga yoziladi.

Xabar qayerda turadi — Dinamik UI'da qulaylik darsidagi qoidalar bo'yicha: xato maydon yonida, aria-invalid="true" va fokus shu maydonda; aria-live — Malika xatoni eshitishi uchun; rang bilan emas, matn bilan.

5. Dasturchiga nima: log

5.1 console.error

Log — dastur ishlaganda yozib boriladigan yozuvlar: nima bo'ldi, qachon, qanday xato. console API darsidagi uch daraja:

Metod Qachon
console.error Bug yoki jiddiy muammo — tuzatish kerak
console.warn G'alati, lekin dastur ishlayapti
console.info / console.log Oddiy voqea, statistika
js
try {
  JSON.parse("{");
} catch (xato) {
  console.error("Bron importi:", xato.name, xato.message);
}

Konsolda:

text
Bron importi: SyntaxError Expected property name or '}' in JSON at position 1 (line 1 column 2)

Bu yerda faqat ikki maydon chiqdi — izni (stack) yo'qotdik. Real kodda xatoning o'zini bering: console.error("Bron importi:", xato). Shunda Node ham, Chrome ham to'liq izni va cause zanjirini ko'rsatadi. Darsda faqat matnni ko'rsatdik, chunki iz qatorlari har kompyuterda boshqacha.

Brauzerdagi console.error ni faqat konsolni ochgan dasturchi ko'radi. Foydalanuvchilar brauzerida chiqqan xatolarni yig'ish uchun saytga xato monitoringi xizmati ulanadi (Production xatolari).

5.2 Logga sir yozilmaydi

Logni ko'p odam o'qiydi: jamoa, monitoring xizmati, ba'zan tashqi kompaniya. Shuning uchun unga parol, token, karta raqami, pasport ma'lumoti va shaxsiy xat yozilmaydi. Faqat kerakli, xavfsiz qismi: "200 ta vazifa", "versiya: 2", "maydon: odam".

5.3 Tuzoq: xato JSON'ga sig'maydi

Logni serverga yuborish uchun uni JSON qilasiz (JSON asoslari). Va kutilmagan narsa chiqadi:

js
class BronXatosi extends Error {
  constructor(xabar, sozlama) {
    super(xabar, sozlama);
    this.name = "BronXatosi";
    this.kod = sozlama?.kod;
  }
}

console.log(JSON.stringify(new Error("Stol band"))); // {}
const bron = new BronXatosi("Stol band", { kod: "BAND" });
console.log(JSON.stringify(bron));

Konsolda:

text
{}
{"name":"BronXatosi","kod":"BAND"}

Oddiy xato — bo'sh obyekt! O'z klassimizdan esa faqat name va kod chiqdi. Sabab: JSON.stringify faqat sanaladigan xususiyatlarni oladi (Xususiyat deskriptorlari). Error esa message, stack va cause ni sanalmaydigan qilib yozadi. Yechim — maydonlarni o'zingiz tanlash:

js
function xatoniSeriyala(xato) {
  if (!(xato instanceof Error)) {
    return { qiymat: String(xato) };
  }
  return {
    name: xato.name,
    message: xato.message,
    kod: xato.kod,
    stack: xato.stack,
    cause: xato.cause === undefined
      ? undefined
      : xatoniSeriyala(xato.cause),
  };
}

const asl = new SyntaxError("buzuq matn");
const xato = new Error("Bron o'qilmadi", { cause: asl });
const log = xatoniSeriyala(xato);

console.log(log.message, "<-", log.cause.name);
console.log(Object.keys(log).join(", "));

Konsolda:

text
Bron o'qilmadi <- SyntaxError
name, message, kod, stack, cause

cause uchun funksiya o'zini chaqiradi (Rekursiya) — zanjir qancha uzun bo'lsa, shuncha chuqur kiradi. Satr tashlangan bo'lsa (throw "matn"), uni matn sifatida saqlaymiz.

Kutubxona kodlarida yana bir vositani uchratasiz: Error.captureStackTrace(obyekt) — istalgan obyektga joriy izni stack sifatida yozadi. U Chrome va Node'da bor, lekin standartda yo'q — o'zingizga odatda kerak emas.

6. Tez yiqil: chegarada tekshir, ichkarida ishon

6.1 Fail fast

Fail fast ("tez yiqil") — noto'g'ri holatni imkon qadar erta aniqlab, shu zahoti to'xtash. Teskarisi — noto'g'ri qiymatni o'tkazib yuborish yoki uni "taxminiy" qiymat bilan almashtirish.

Nega erta yaxshi? Noto'g'ri qiymat kod bo'ylab qancha uzoq yursa, xato shuncha uzoqda va g'alati ko'rinishda chiqadi (Stack trace darsidagi "yiqilgan joy va aybdor joy" farqi).

Misol vazifalar dan. Prototype pollution darsidagi vazifaniOl yetishmagan id ga 0 qo'yadi. id siz ikki vazifa import qilinsa — ikkalasi id: 0 oladi. Keyin bitta bosish ikki vazifani o'zgartiradi — xato import paytida emas, butunlay boshqa joyda chiqadi. Fail fast esa importning o'zida "1-vazifa: id yo'q" deb to'xtaydi.

6.2 Himoyaviy dasturlash — qayerda

Himoyaviy dasturlash (defensive programming) — kirishga ishonmaslik, uni tekshirish. Lekin hamma joyda emas:

  • Chegarada — qattiq. Tashqaridan kelgan har narsa (foydalanuvchi, fayl, tarmoq, JSON.parse natijasi) — turi, oralig'i, tuzilmasi bilan tekshiriladi.
  • Ichkarida — ishonch. Chegaradan o'tgan ma'lumot tekshirilgan. Har ichki funksiyada typeof ni qayta yozish kodni shishiradi.

Ichkarida bitta istisno bor: invariant — "bu yerda doim rost bo'lishi shart" bo'lgan narsa. U buzilsa — bu bug, uni ham darhol ko'rish kerak:

js
function vazifaniYangila(vazifalar, id, matn) {
  const orin = vazifalar.findIndex((v) => v.id === id);
  if (orin === -1) {
    throw new Error(`Invariant buzildi: ${id}-vazifa yo'q`);
  }
  return vazifalar.with(orin, { ...vazifalar[orin], matn });
}

const royxat = [{ id: 1, matn: "Non olish" }];
const yangi = vazifaniYangila(royxat, 1, "Non va sut");
console.log(yangi[0].matn); // Non va sut

Tugma faqat mavjud vazifaga chiziladi — demak -1 bo'lishi mumkin emas. Lekin bo'lib qolsa (bug!), with(-1, …) jimgina oxirgi vazifani almashtirardi (Ichma-ich ma'lumotni immutable yangilash darsidagi tuzoq). throw bilan esa bug darhol ko'rinadi. Bu xabar dasturchi uchun — shuning uchun oddiy Error va texnik matn.

6.3 Hammasi yoki hech narsa

Holat yarim o'zgarib qolmasin. Import vazifalarni birma-bir qo'shib borsa-yu, o'ninchisida xato chiqsa — ro'yxatda to'qqiz yangi va eskilar aralash qoladi. Na eski holat, na yangi.

Qoida: avval hammasini tekshir, keyin bir zarbda o'zgartir. Tekshiruv faqat o'qiydi va xato tashlaydi. Holatni o'zgartiradigan bitta qator — eng oxirida. Ma'lumotlar bazalarida bu g'oya tranzaksiya deyiladi — uni SQL qismida o'rganamiz.

7. Result naqshi: throw siz xato

7.1 Natija obyekti

Ba'zan xatoni qiymat sifatida qaytarish qulayroq. Funksiya doim obyekt qaytaradi: muvaffaqiyatda { ok: true, qiymat }, xatoda { ok: false, xatolar }. Bu Result naqshi ("natija naqshi") deyiladi.

Eng yaxshi misol — forma tekshiruvi. throw birinchi xatoda to'xtaydi: mehmon "ism bo'sh" ni tuzatadi, yuboradi, endi "odam soni ko'p" ni ko'radi... Uch marta yuborish o'rniga hamma xatoni birdan ko'rsatish yaxshi:

js
function bronniTekshir(bron) {
  const xatolar = [];
  if (bron.ism.trim() === "") {
    xatolar.push({ maydon: "ism", xabar: "Ismingizni yozing." });
  }
  if (!(bron.odam >= 1 && bron.odam <= 20)) {
    xatolar.push({ maydon: "odam", xabar: "1–20 kishi." });
  }
  if (bron.vaqt < "10:00" || bron.vaqt > "22:00") {
    xatolar.push({ maydon: "vaqt", xabar: "Soat 10:00–22:00." });
  }
  if (xatolar.length > 0) {
    return { ok: false, xatolar };
  }
  return { ok: true, qiymat: bron };
}

const natija = bronniTekshir({ ism: "", odam: 25, vaqt: "19:00" });
if (natija.ok) {
  console.log("Bron:", natija.qiymat.ism);
} else {
  for (const x of natija.xatolar) {
    console.log(`${x.maydon}: ${x.xabar}`);
  }
}

Konsolda:

text
ism: Ismingizni yozing.
odam: 1–20 kishi.

7.2 throw yoki Result?

throw Result
Xatoni e'tiborsiz qoldirish Mumkin emas — dastur to'xtaydi Mumkin — ok ni tekshirmasa
Bir nechta xato Faqat birinchisi Hammasi birdan
Chaqiruvchi kodi try...catch if (natija.ok)
Qayerda ko'p Kutilmagan va jiddiy holatlar Forma tekshiruvi

Ikkalasi bir loyihada yonma-yon yashaydi. Result'ning to'liq shakli — Xato va bo'sh qiymatlar bilan FP uslubida ishlash darsida.

Tekshirib ko'ring: Result qaytaradigan funksiyani chaqirib, natija.qiymat.ism ni ok ni tekshirmasdan o'qisa nima bo'ladi?

Javob

Xato bo'lgan holatda natija.qiymat — undefined, va undefined.ism — TypeError. Ya'ni operatsion xato e'tiborsiz qoldirildi va uzoqroq joyda bug'ga aylandi. Result naqshining asosiy xavfi shu: tekshiruvni chaqiruvchi eslab qolishi kerak. TypeScript (15-qism) bu tekshiruvni majburiy qila oladi.

7.3 Brauzerda: hamma xato birdaniga

Xuddi shu bronniTekshir bron formasida. Xatolar ro'yxat bo'lib chiqadi, birinchi noto'g'ri maydonga fokus o'tadi:

html
<style>
  body { margin: 1rem; font: 1rem/1.5 system-ui, sans-serif; }
  input, button { font: inherit; min-height: 2.75rem; }
  [aria-invalid="true"] { outline: 3px solid #b3261e; }
  #xatolar { color: #b3261e; }
</style>
<form id="bron" novalidate>
  <label for="ism">Ism</label>
  <input id="ism" name="ism">
  <label for="odam">Necha kishi?</label>
  <input id="odam" name="odam" type="number" value="25">
  <label for="vaqt">Soat</label>
  <input id="vaqt" name="vaqt" type="time" value="23:00">
  <button>Bron qilish</button>
</form>
<ul id="xatolar" aria-live="polite"></ul>
<p id="javob" role="status"></p>
<script>
  function bronniTekshir(bron) {
    const xatolar = [];
    if (bron.ism.trim() === "") {
      xatolar.push({ maydon: "ism", xabar: "Ismingizni yozing." });
    }
    if (!(bron.odam >= 1 && bron.odam <= 20)) {
      xatolar.push({ maydon: "odam", xabar: "1–20 kishi." });
    }
    if (bron.vaqt < "10:00" || bron.vaqt > "22:00") {
      xatolar.push({ maydon: "vaqt", xabar: "Soat 10:00–22:00." });
    }
    return xatolar.length > 0
      ? { ok: false, xatolar }
      : { ok: true, qiymat: bron };
  }

  const forma = document.getElementById("bron");
  const royxat = document.getElementById("xatolar");
  const javob = document.getElementById("javob");

  forma.addEventListener("submit", (event) => {
    event.preventDefault();
    const natija = bronniTekshir({
      ism: forma.ism.value,
      odam: Number(forma.odam.value),
      vaqt: forma.vaqt.value,
    });
    for (const nom of ["ism", "odam", "vaqt"]) {
      forma[nom].removeAttribute("aria-invalid");
    }
    javob.textContent = "";
    const xatolar = natija.ok ? [] : natija.xatolar;
    royxat.replaceChildren(...xatolar.map((x) => {
      forma[x.maydon].setAttribute("aria-invalid", "true");
      const li = document.createElement("li");
      li.textContent = x.xabar;
      return li;
    }));
    if (natija.ok) {
      javob.textContent = `${natija.qiymat.ism}, bron qabul qilindi.`;
    } else {
      forma[natija.xatolar[0].maydon].focus();
    }
  });
</script>

Sinab ko'ring:

  • «Bron qilish» — uch xato birdaniga, uch maydon qizil, fokus «Ism» da.
  • Ismni yozing va qayta bosing — ikki xato qoldi, fokus «Necha kishi?» da.
  • Hammasini to'g'rilang — ro'yxat bo'shaydi, "…, bron qabul qilindi.".

throw bilan mehmon uch marta yuborardi. Result bilan — bir marta, va nimani tuzatishni birdan ko'radi.

8. Ko'p uchraydigan xatolar

Xato Nima bo'ladi Tuzatish
Har funksiyada try...catch Xatolar hamma joyda "hal qilinadi", hech qayerda ko'rinmaydi try faqat chegarada va o'rash kerak joyda
catch da return null Sabab yo'qoladi, chaqiruvchi "nega null?" deb hayron Ushlamang yoki Result / o'z xato turingiz
Har xatoda importXatosiniKorsat(xato.message) Mehmon Cannot read properties… ni ko'radi, dasturchi hech narsani O'z turingiz — ko'rsatish; boshqasi — umumiy xabar va qayta tashlash
console.error("Kirish", { login, parol }) Parol log faylida va monitoringda Faqat kerakli, xavfsiz maydonlar
JSON.stringify(xato) bilan log Natija {} xatoniSeriyala kabi funksiya
Yo'q id ga 0, yo'q narxga 0 Xato keyinroq, boshqa joyda chiqadi Chegarada fail fast

9. Mashqlar

1-mashq (oson): Operatsionmi yoki bug?

Har holat uchun ayting: operatsion xatomi yoki bug, va to'g'ri javob nima (ko'rsatish, qayta urinish, tuzatish).

  1. Foydalanuvchi bron formasida 31-fevralni tanladi.
  2. render da royxat.replaceChildren(...) o'rniga royxat.replaceChildrens(...).
  3. Server 30 soniya javob bermadi.
  4. Import qilingan JSON'da "versiya": 3.
  5. vazifaniTop(id) undefined qaytardi, garchi tugma shu id bilan chizilgan bo'lsa ham.
Yechim
  1. Operatsion — foydalanuvchi kiritishi. Maydon yonida xabar va fokus.
  2. Bug — metod nomida xato (TypeError: … is not a function). Kodni tuzatish.
  3. Operatsion — tarmoq. Xabar ("Server javob bermadi, qayta urinib ko'ring") va ehtimol avtomatik qayta urinish (11-qismda).
  4. Operatsion — tashqi fayl. VazifaXatosi, kod VERSIYA, tushunarli xabar.
  5. Bug — invariant buzilgan: holat va ekran bir-biriga mos emas. Uni yashirmasdan throw qilish va sababini topish.

2-mashq (o'rta): Xavfsiz log

logYoz(amal, xato, malumot) funksiyasini yozing. U console.error ga bitta qator chiqarsin: amal nomi, xato turi va matni, hamda malumot obyektidan faqat oq ro'yxatdagi kalitlar (["stol", "odam", "vaqt"]). Parol yoki telefon logga tushmasin. Ishora: ro'yxatni filter bilan, obyektda kalit borligini Object.hasOwn bilan tekshiring.

Yechim
js
const LOG_MAYDONLARI = ["stol", "odam", "vaqt"];

function logYoz(amal, xato, malumot) {
  const xavfsiz = LOG_MAYDONLARI
    .filter((k) => Object.hasOwn(malumot, k))
    .map((k) => `${k}=${malumot[k]}`)
    .join(" ");
  const sabab = `${xato.name}: ${xato.message}`;
  console.error(`[${amal}] ${sabab} | ${xavfsiz}`);
}

const bron = {
  stol: 4,
  odam: 25,
  telefon: "+998 90 000 00 00",
  parol: "maxfiy",
};

logYoz("bron", new RangeError("Ko'p odam"), bron);

Konsolda:

text
[bron] RangeError: Ko'p odam | stol=4 odam=25

telefon va parol logga tushmadi — oq ro'yxat ularni bilmaydi. vaqt obyektda yo'q, hasOwn uni o'tkazib yubordi. Nega qora ro'yxat ("parolni olib tashla") emas? Yangi maxfiy maydon qo'shilsa, oq ro'yxatda u o'z-o'zidan logga tushmaydi — xuddi Prototype pollution darsidagidek.

3-mashq (qiyin): Result bilan kunlik import

Kun oxirida bronlar matnlar massivi sifatida keladi. bronlarniOqi(matnlar) funksiyasi Result qaytarsin: { ok: true, qiymat: [...] } yoki { ok: false, xatolar: [...] }. Har xato — { tartib, xabar }: tartib — 1 dan boshlanadigan raqam, xabar — "JSON emas" yoki "odam 1–20 emas". Hammasi yoki hech narsa: bitta xato bo'lsa ham, qiymat qaytmasin. Ishora: har matn uchun try, natijalarni ikki massivga yig'ing.

Yechim
js
function bronlarniOqi(matnlar) {
  const bronlar = [];
  const xatolar = [];
  matnlar.forEach((matn, i) => {
    let bron;
    try {
      bron = JSON.parse(matn);
    } catch {
      xatolar.push({ tartib: i + 1, xabar: "JSON emas" });
      return;
    }
    if (!(bron?.odam >= 1 && bron.odam <= 20)) {
      xatolar.push({ tartib: i + 1, xabar: "odam 1–20 emas" });
      return;
    }
    bronlar.push(bron);
  });
  if (xatolar.length > 0) {
    return { ok: false, xatolar };
  }
  return { ok: true, qiymat: bronlar };
}

console.log(bronlarniOqi(['{"odam": 4}', '{"odam": 2}']));
console.log(bronlarniOqi(['{"odam": 4}', "{", '{"odam": 30}']));

Konsolda:

text
{ ok: true, qiymat: [ { odam: 4 }, { odam: 2 } ] }
{
  ok: false,
  xatolar: [
    { tartib: 2, xabar: 'JSON emas' },
    { tartib: 3, xabar: 'odam 1–20 emas' }
  ]
}

Ikkinchi chaqiruvda birinchi bron to'g'ri edi, lekin qiymat qaytmadi — hammasi yoki hech narsa. forEach callback'idagi return faqat shu elementni tugatadi, siklni emas (Callback imzosi). bron?.odam — matn "null" bo'lsa ham yiqilmaydi.

4-mashq: Vazifalar qadami — importni yakunlash

feature/import branch'idagi oxirgi ish. try / catch darsidagi ochiq muammo hamon turibdi: "bajarildi": "ha" qabul qilinadi, id siz vazifalar id: 0 oladi. Bug'da esa foydalanuvchi hech qanday xabar olmaydi (O'z xato klasslaringiz).

Talablar:

  1. Fail fast. paketniOqi qaytarishdan oldin har vazifani tekshirsin: id — musbat butun son va takrorlanmas; matn — bo'sh bo'lmagan satr, Vazifa.MAKS_UZUNLIK dan uzun emas; bajarildi — true yoki false. Birinchi buzilish — VazifaXatosi, kod VAZIFA, xabar qaysi vazifa ekanini aytsin ("2-vazifa: …").
  2. Hammasi yoki hech narsa. Ro'yxat faqat hamma tekshiruvdan keyin almashadi.
  3. Bug'ga ham xabar. Begona xatoda — maydon yonida umumiy "Kutilmagan xato: import bajarilmadi." va qayta tashlash.
Yechim

"3. Hisoblovchi funksiyalar" ga — paketniOqi dan oldin:

js
function vazifaXatosi(tartib, sabab) {
  return new VazifaXatosi(`${tartib}-vazifa: ${sabab}`, {
    kod: "VAZIFA",
  });
}

function malumotlarniTekshir(malumotlar) {
  const idlar = new Set();
  malumotlar.forEach((v, i) => {
    const tartib = i + 1;
    if (!Number.isInteger(v.id) || v.id < 1 || idlar.has(v.id)) {
      throw vazifaXatosi(tartib, "id musbat, takrorlanmas son emas.");
    }
    idlar.add(v.id);
    if (typeof v.matn !== "string" || v.matn.trim() === "") {
      throw vazifaXatosi(tartib, "matn bo'sh.");
    }
    if (v.matn.length > Vazifa.MAKS_UZUNLIK) {
      throw vazifaXatosi(tartib, "matn juda uzun.");
    }
    if (typeof v.bajarildi !== "boolean") {
      throw vazifaXatosi(tartib, "bajarildi true yoki false emas.");
    }
  });
}

paketniOqi ning oxiri:

js
  const malumotlar = paket.vazifalar.map(vazifaniOl);
  malumotlarniTekshir(malumotlar);
  return malumotlar;
}

Tinglovchidagi catch:

js
  } catch (xato) {
    if (xato instanceof VazifaXatosi) {
      importXatosiniKorsat(xato.message);
      return;
    }
    importXatosiniKorsat("Kutilmagan xato: import bajarilmadi.");
    throw xato;
  }

Har qaror — darsning bir bo'limi:

  • Fail fast, chegarada. Tekshiruv vazifaniOl dan keyin keladi: oq ro'yxat begona kalitlarni tashladi, tekshiruv qolganining turini ko'radi. vazifaniOl qo'ygan id: 0 darhol rad etiladi. Vazifa va VazifalarRoyxati ichida qayta tekshirish shart emas.
  • vazifaXatosi — fabrika funksiya. To'rt throw bir xil shaklda, xabar formati bitta joyda. Kod ham bitta — VAZIFA, alohida klass kerak emas.
  • Set bilan takror id. Set "bu id ni ko'rdimmi?" savoliga darhol javob beradi. Takror id — eng xavfli holat: top(id) ikkinchisini hech qachon topmaydi.
  • Hammasi yoki hech narsa. malumotlarniTekshir holatga tegmaydi, royxatniAlmashtir esa tinglovchida, paketniOqi dan keyin turadi. Uchinchi vazifa buzuq bo'lsa, eski ro'yxat joyida qoladi.
  • Bug: ikki tomonga xabar. Foydalanuvchi umumiy matnni ko'radi, dasturchi esa throw xato tufayli konsolda to'liq Uncaught xatoni.

Tekshiruvni Node'da sinang. Vazifa.MAKS_UZUNLIK o'rniga 100 — klass bu sinovda kerak emas:

js
class VazifaXatosi extends Error {
  constructor(xabar, sozlama) {
    super(xabar, sozlama);
    this.name = "VazifaXatosi";
    this.kod = sozlama?.kod ?? "NOMALUM";
  }
}

const MAKS = 100;

function malumotlarniTekshir(malumotlar) {
  const idlar = new Set();
  malumotlar.forEach((v, i) => {
    const xato = (sabab) =>
      new VazifaXatosi(`${i + 1}-vazifa: ${sabab}`, {
        kod: "VAZIFA",
      });
    if (!Number.isInteger(v.id) || v.id < 1 || idlar.has(v.id)) {
      throw xato("id musbat, takrorlanmas son emas.");
    }
    idlar.add(v.id);
    if (typeof v.matn !== "string" || v.matn.trim() === "") {
      throw xato("matn bo'sh.");
    }
    if (v.matn.length > MAKS) {
      throw xato("matn juda uzun.");
    }
    if (typeof v.bajarildi !== "boolean") {
      throw xato("bajarildi true yoki false emas.");
    }
  });
}

const holatlar = [
  [{ id: 1, matn: "Non olish", bajarildi: false }],
  [{ id: 0, matn: "id siz", bajarildi: false }],
  [{ id: 1, matn: "A", bajarildi: false }, { id: 1, matn: "B" }],
  [{ id: 2, matn: "   ", bajarildi: true }],
  [{ id: 3, matn: "x".repeat(101), bajarildi: true }],
  [{ id: 4, matn: "Choy", bajarildi: "ha" }],
];

for (const malumotlar of holatlar) {
  try {
    malumotlarniTekshir(malumotlar);
    console.log("To'g'ri");
  } catch (xato) {
    console.log(`${xato.kod}: ${xato.message}`);
  }
}

Konsolda:

text
To'g'ri
VAZIFA: 1-vazifa: id musbat, takrorlanmas son emas.
VAZIFA: 2-vazifa: id musbat, takrorlanmas son emas.
VAZIFA: 1-vazifa: matn bo'sh.
VAZIFA: 1-vazifa: matn juda uzun.
VAZIFA: 1-vazifa: bajarildi true yoki false emas.

Brauzerda biz import imkoniyatini Chrome 154 da shu tartibda sinab ko'rdik:

Import qilingan matn Natija
O'z eksportingiz Ro'yxat eksport paytidagi holatiga qaytdi; "3 ta vazifa import qilindi."
id: 5 va id: 9, keyin yangi vazifa Yangisi id: 10 oladi
{"versiya": 1, "vazifalar": [} yoki bo'sh maydon "Matn JSON emas…", ro'yxat o'zgarmadi
{"versiya": 2, …} "Versiya 2 o'qilmaydi — faqat 1."
__proto__ va constructor kalitli paket Vazifa o'qildi, ({}).admin — undefined
Takror id, bo'sh matn, "bajarildi": "ha" "2-vazifa: id musbat, takrorlanmas son emas.", ro'yxat o'zgarmadi
DevTools'da buzilgan VazifalarRoyxati.dan Maydon yonida "Kutilmagan xato…", konsolda Uncaught TypeError

Xatolar moduli tugadi — branch'ni yakunlang:

bash
git add assets/js/vazifalar.js
git commit -m "Vazifalar: import ma'lumotini tekshir"
git switch main
git merge feature/import
git branch -d feature/import
git push

To'rt darsda vazifalar importi qanday qurildi:

Dars Qo'shildi
try / catch / finally Import UI, try ichida paketniOqi, royxatniAlmashtir + obuna
throw va Error Jim [] o'rniga throw, cause bilan JSON xatosi
O'z xato klasslaringiz VazifaXatosi + kod, begona xato qayta tashlanadi
shu dars Vazifa tekshiruvi (fail fast), bug uchun umumiy xabar

Pages bir-ikki daqiqada yangilanadi. Endi ro'yxatni bir kompyuterdan eksport qilib, boshqasida import qilsa bo'ladi.

10. Real ishda

  • Serverda (Express, NestJS — kursda keyinroq) chegarani freymvorkning o'zi beradi: har so'rov uchun bitta umumiy xato ishlovchisi. Bu — bugungi tinglovchining server shakli.
  • Xato monitoringi. Sentry kabi xizmatlar ushlanmagan xatolarni foydalanuvchilar brauzeridan cause va izi bilan yig'adi (Production xatolari). Shuning uchun bug'ni qayta tashlash — uni ko'rinadigan qilish.
  • React'da (keyingi qismlar) chegara nomida bor — "Error Boundary": sahifaning bir qismi yiqilsa, faqat o'sha qism o'rniga "Nimadir buzildi" chiqadi.
  • Intervyu. "Operatsion xato va bug farqi?", "xatoni qayerda ushlaysiz?", "xato matnini foydalanuvchiga ko'rsatish nega xavfli?" — tez-tez so'raladi.

Xulosa

  • Operatsion xato — kutilgan holat, unga javob beriladi; bug — kod kamchiligi, tuzatiladi. Farq turda emas, "kim aybdor" da.
  • catch faqat hal qila, ko'rsata yoki ma'no qo'shib qayta tashlay olsangiz yoziladi; ko'rsatish — chegarada.
  • Foydalanuvchiga: nima bo'ldi va nima qilish kerak, aria-live bilan; stack va ichki matn — yo'q.
  • Dasturchiga: console.error(xato) va monitoring; logga sir yozilmaydi; JSON.stringify(xato) — {}.
  • Fail fast: chegarada tekshiring, ichkarida ishoning, invariant buzilsa — throw. Holat hamma tekshiruvdan keyin bir zarbda o'zgaradi.
  • Result ({ ok, qiymat | xatolar }) — hamma xatoni birdan yig'ish uchun; throw — jiddiy va kutilmagan holatlar uchun.

Keyingi dars: Resurslarni aniq boshqarish: using — try...finally dagi "albatta yop" ishini avtomatlashtiradigan yangi sintaksis.

Manbalar

  • MDN: "Control flow and error handling", "console.error()" — developer.mozilla.org
  • Node.js: "Errors" — nodejs.org/api/errors.html
  • OWASP: "Error Handling Cheat Sheet", "Logging Cheat Sheet" — cheatsheetseries.owasp.org
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript'da xato strategiyasi: qayerda ushlash, nima ko'rsatish va nimani loglash — IlmHamroh