IlmHamroh
JavaScript Full-stack/10-qism. JavaScript chuqur55/65-dars24 daqiqa
Mundarija (33)

JavaScript try...catch...finally: xatoni ushlash va dasturni yiqitmaslik

Qisqacha: Xato chiqishi mumkin bo'lgan kodni try { ... } ichiga yozasiz. Xato chiqsa, JavaScript try ning qolgan qatorlarini tashlab, catch (xato) { ... } ga o'tadi — dastur yiqilmaydi. Shundan keyin finally { ... } har doim bajariladi: xato bo'lsa ham, bo'lmasa ham. catch faqat o'zi hal qila oladigan xatoni ushlashi kerak, qolganini throw xato bilan yuqoriga qaytaradi.

Bu darsda

  • try / catch bilan JSON.parse kabi xavfli chaqiruvni o'rab, dasturni yiqitmasdan ishlata olasiz.
  • Xato chiqqanda kod qaysi tartibda bajarilishini qadamma-qadam tushuntira olasiz.
  • catch parametridan xato turini va matnini o'qiysiz, keraksiz bo'lsa uni tashlab yozasiz (catch { }).
  • finally ni qachon ishlatishni va undagi return nega xavfli ekanini bilasiz.
  • Begona xatoni throw xato bilan qayta tashlab, uni "yutib yubormaslikni" o'rganasiz.
  • vazifalar ga JSON import qo'shasiz.

Oldin bilishingiz kerak: Stack trace va xato turlari, JSON asoslari, JSON chuqur, Execution context va call stack, Prototype pollution.

1. Nega bu kerak?

JSON asoslari darsida JSON.parse buzilgan matnni ko'rsa, SyntaxError bilan hamma ishni to'xtatishini ko'rdik. O'shanda "xatoni ushlashni keyin o'rganamiz" degan edik. O'sha "keyin" — bugun.

«Bahor»dagi vaziyat. Ofitsiant Otabek buyurtmani planshetga kiritadi, planshet uni JSON matni qilib oshxonaga yuboradi. Bir kuni planshet zaryadi tugab, matnning faqat yarmi yetib keldi. Oshxona dasturi Node'da ishlaydi va matnni JSON.parse bilan o'qiydi:

js
const kelgan = '{"stol": 4';
const buyurtma = JSON.parse(kelgan);

console.log(buyurtma.stol);
console.log("Keyingi buyurtmani kutyapman...");
text
SyntaxError: Expected ',' or '}' after property value in JSON at position 10 (line 1 column 11)

Ikkala console.log ham ishlamadi. Stack trace darsidan bilasiz: Node'da ushlanmagan xato butun dasturni to'xtatadi. Ya'ni bitta buzuq buyurtma tufayli oshxona ekrani o'chdi. Keyingi o'nta to'g'ri buyurtma ham endi kelmaydi.

Bu yerda kodda xato yo'q. Buzilgan narsa — tashqaridan kelgan ma'lumot. Uni oldindan to'liq tekshirib bo'lmaydi: matnning to'g'ri JSON ekanini bilishning yagona ishonchli yo'li — uni o'qib ko'rish. Demak bizga shunday vosita kerak: "o'qib ko'r, xato chiqsa — dasturni yiqitma, menga ayt".

Hayotdan o'xshatish: sirkdagi xavfsizlik to'ri. Akrobat baland arqonda yuradi. U yiqilishi mumkin, shuning uchun pastga to'r tortiladi. Yiqilsa — to'rga tushadi, tomosha davom etadi. try — "arqon ustidagi" xavfli harakat, catch — to'r.

2. Birinchi try...catch

2.1 Kod

Xuddi shu buzuq buyurtma, endi to'r bilan:

js
const kelgan = '{"stol": 4';

try {
  const buyurtma = JSON.parse(kelgan);
  console.log("Qabul qilindi:", buyurtma.stol);
} catch (xato) {
  console.log("Buyurtma o'qilmadi:", xato.name);
}

console.log("Keyingi buyurtmani kutyapman...");

Konsolda:

text
Buyurtma o'qilmadi: SyntaxError
Keyingi buyurtmani kutyapman...

Dastur yiqilmadi. Qismlarini ko'raylik:

  • try { ... } — "sinab ko'r" bloki. Ichidagi kod odatdagidek bajariladi.
  • catch (xato) { ... } — "ushla" bloki. U faqat try ichida xato chiqqanda ishlaydi.
  • xato — ushlangan xato obyekti. Nomini o'zingiz tanlaysiz, xuddi funksiya parametri kabi. Kodlarda ko'pincha error, err yoki e deyiladi; biz xato deymiz.
  • Blokdan keyingi kod — console.log("Keyingi...") — xato bo'lsa ham, bo'lmasa ham davom etadi.

Atama: xato "tashlanadi" va "ushlanadi". JSON.parse buzuq matnni ko'rganda xato tashlaydi (throw). Tashlangan xatoni boshqa ko'p tillarda istisno (exception) deyishadi — dasturning odatdagi yo'lidan "istisno" holat. catch uni ushlaydi (catch). O'zingiz xato tashlashni keyingi darsda o'rganasiz. Bugun JavaScript'ning o'zi tashlaydigan xatolarni ushlaymiz.

Endi kelgan ni to'g'ri matnga almashtiring: '{"stol": 4}'. Natija — Qabul qilindi: 4 va Keyingi buyurtmani kutyapman.... catch bloki umuman ishlamadi.

2.2 catch parametri — xato obyekti

Stack trace darsida ko'rgansiz: xato — obyekt, uning name (turi) va message (matni) bor. catch aynan shu obyektni oladi:

js
try {
  JSON.parse("Manti");
} catch (xato) {
  console.log(xato.name);
  console.log(xato.message);
  console.log(xato instanceof SyntaxError);
}

Konsolda:

text
SyntaxError
Unexpected token 'M', "Manti" is not valid JSON
true

Konsolga chiqqan matn — konsolda qizil xato bo'lib chiqadigan matnning aynan o'zi, faqat endi u qiymat. Uni o'qish, solishtirish, ekranga chiqarish mumkin. instanceof — "shu klassdan yaratilganmi?" tekshiruvi (Object.create va instanceof). SyntaxError ham klass, va u Error dan meros oladi (Meros).

Bitta muhim fikr: xato.message — dasturchi uchun yozilgan, inglizcha matn. Uni o'zgarishsiz foydalanuvchiga ko'rsatish yaxshi emas: Otabek "Unexpected token" dan hech narsa tushunmaydi. Foydalanuvchiga o'z tilidagi, nima qilish kerakligini aytadigan xabar ko'rsatiladi. Bu qoidaga Xato strategiyasi darsida qaytamiz.

Tekshirib ko'ring: try ichida xato bo'lmasa, catch dagi kod bajariladimi? catch dan keyingi kod-chi?

Javob

catch dagi kod bajarilmaydi — u faqat xato chiqqanda ishlaydi. catch dan keyingi kod esa ikkala holatda ham bajariladi: try...catch bloki tugagach, dastur odatdagidek pastga davom etadi.

3. Bajarilish tartibi

3.1 Qadamma-qadam

Xato chiqqan zahoti nima bo'ladi? try ning qolgan qatorlari tashlab ketiladi. Quyidagi kodda raqamlangan xabarlar bor. Keyingi tugmasini bosing va 3-xabar nega chiqmasligiga qarang:

Uchta qoida:

  1. Xato chiqqan qatordan keyingi try qatorlari bajarilmaydi. Xatodan oldingi qatorlar esa bajarilgan bo'ladi — ularni "orqaga qaytarib" bo'lmaydi.
  2. Xato bo'lsa — catch, bo'lmasa — catch o'tkazib yuboriladi.
  3. finally (bu blok haqida «finally» bo'limida) va blokdan keyingi kod — ikkala holatda ham.

Birinchi qoidaga e'tibor bering. Stack trace darsida savat misolida jami ga narx qo'shilib ulgurgan, ekran esa yangilanmagan edi. try ham shunday: u xatodan oldin bajarilgan ishni bekor qilmaydi. Holat yarim o'zgargan bo'lib qolishi mumkin. Shuning uchun xavfli qadamni holatni o'zgartirishdan oldin qo'ying — «Vazifalar qadami» da aynan shunday qilamiz.

Butun yo'l bitta sxemada:

flowchart TD
  T["try bloki"] --> Q{"Xato chiqdimi?"}
  Q -- "yo'q" --> F["finally"]
  Q -- "ha: try ning qolgani tashlanadi" --> C["catch (xato)"]
  C --> F
  F --> D["blokdan keyingi kod"]

3.2 Xato chaqiruvlar steki bo'ylab ko'tariladi

Xato try ning o'zida emas, u chaqirgan funksiyaning ichida chiqsa-chi? Mehmon menyuda yo'q taomni so'radi:

js
const menyu = {
  osh: { narx: 35000 },
  manti: { narx: 30000 },
};

function narxniOl(taom) {
  return menyu[taom].narx;
}

function chekniHisobla(taomlar) {
  return taomlar.reduce((jami, t) => jami + narxniOl(t), 0);
}

try {
  console.log(chekniHisobla(["osh", "manti"])); // 65000
  console.log(chekniHisobla(["osh", "shashlik"]));
} catch (xato) {
  console.log("Chek hisoblanmadi:", xato.message);
}

Konsolda:

text
65000
Chek hisoblanmadi: Cannot read properties of undefined (reading 'narx')

Xato narxniOl da chiqdi — u yerda try yo'q. JavaScript shunda xatoni chaqiruvchiga uzatadi: reduce ning callback'iga, so'ng reduce ga, so'ng chekniHisobla ga. Hech biri ushlamadi — keyingi qavat. Eng tepadagi try uni ushladi. Yo'ldagi har funksiya o'sha zahoti to'xtadi: chekniHisobla natija qaytarmadi.

Bu — Execution context va call stack darsidagi stekning teskari yo'li. Xato stekning tepasidan pastga qarab, birinchi catch gacha "ko'tariladi". Hech kim ushlamasa — stek bo'shaydi va xato Uncaught bo'ladi.

flowchart BT
  A["narxniOl: xato chiqdi"] -- "ushlamadi" --> B["reduce callback"]
  B -- "ushlamadi" --> C["chekniHisobla"]
  C -- "ushlamadi" --> D["try bloki — catch ushladi"]

Amaliy xulosa: try ni har funksiyaga qo'yish shart emas. Uni xatoga javob bera oladigan joyga — bu yerda chekni ekranga chiqaradigan joyga — qo'yasiz. Qayerga qo'yish strategiyasi — Xato strategiyasi darsida.

Tekshirib ko'ring: Yuqoridagi kodda ikkinchi chekniHisobla ning "osh" uchun narxi hisoblanganmi? Natija qayerga ketdi?

Javob

Hisoblangan: reduce birinchi qadamda 0 + 35000 ni oldi. Ikkinchi qadamda narxniOl("shashlik") xato tashladi va reduce to'xtadi. Oraliq akkumulyator hech qayerga qaytmadi — u yo'qoldi. console.log ham chaqirilmadi: uning argumenti hisoblanmay qoldi.

4. try ichidagi o'zgaruvchi tashqarida ko'rinmaydi

try { } — oddiy blok. Uning ichida const yoki let bilan e'lon qilingan nom faqat shu blok ichida yashaydi (Leksik scope):

js
try {
  const buyurtma = JSON.parse('{"taom": "Osh"}');
} catch (xato) {
  console.log("Buzuq matn");
}

console.log(buyurtma.taom);
text
ReferenceError: buyurtma is not defined

Matn to'g'ri edi, parse ishladi — lekin buyurtma blok tugashi bilan yo'qoldi. Ikki yechim bor.

Birinchisi — natijani ishlatadigan kodni ham try ichiga olish. Kichik kodda shunday qilinadi.

Ikkinchisi — o'zgaruvchini blokdan oldin let bilan e'lon qilib, ichida qiymat berish:

js
let buyurtma;
try {
  buyurtma = JSON.parse('{"taom": "Osh"}');
} catch (xato) {
  console.log("Buzuq matn");
}

console.log(buyurtma.taom); // Osh

Bu usul try ni kichik saqlaydi: ichida faqat xato kutilgan bitta qator. Nega bu muhim — «Ko'p uchraydigan xatolar» da ko'ramiz. Lekin bir narsani unutmang: xato bo'lsa, buyurtma undefined bo'lib qoladi. catch ichida bu holatni hal qiling — masalan, return bilan funksiyadan chiqing. «Vazifalar qadami» da aynan shu qolipni ishlatamiz.

catch (xato) dagi xato ham faqat catch bloki ichida ko'rinadi.

5. catch parametrisiz: optional catch binding

Ba'zan xato obyekti kerak emas — faqat "xato bo'ldimi?" muhim. Masalan, predikat — true/false qaytaradigan funksiya:

js
function jsonmi(matn) {
  try {
    JSON.parse(matn);
    return true;
  } catch {
    return false;
  }
}

console.log(jsonmi('{"taom": "Osh"}')); // true
console.log(jsonmi("{taom: 'Osh'}")); // false
console.log(jsonmi("")); // false

catch dan keyin qavs yo'q — bu optional catch binding ("ixtiyoriy bog'lash"). "Bog'lash" — xato obyektini (xato) kabi nomga bog'lash; endi bu majburiy emas. ES2019 da qo'shilgan; Baseline ma'lumotiga ko'ra 2020-yil yanvardan barcha asosiy brauzerlarda bor, 2022-yil iyuldan esa keng qo'llab-quvvatlanadi. Xato obyektini ishlatmasangiz, uni yozmang: o'qigan odam "bu yerda xato turi ahamiyatsiz" deb darhol tushunadi.

Ehtiyot bo'ling: catch { } — "har qanday xato bo'lsa". jsonmi uchun bu to'g'ri, chunki try da faqat JSON.parse turibdi. try katta bo'lsa, parametrsiz catch boshqa xatolarni ham jimgina yutib yuboradi.

Tekshirib ko'ring: jsonmi("35000") va jsonmi("null") nima qaytaradi?

Javob

Ikkalasi ham true. 35000 — to'g'ri JSON (son), null ham to'g'ri JSON (JSON asoslari darsidagi "matn ichida faqat bitta qiymat"). jsonmi faqat "o'qiladimi?" deb so'raydi, "bu buyurtmami?" deb emas. Matn kerakli tuzilmada ekanini alohida tekshirish kerak — bu «Vazifalar qadami» va keyingi darslarning mavzusi.

6. finally: har doim bajariladigan blok

6.1 Nima uchun kerak

Ba'zi ishlar natijadan qat'i nazar bajarilishi shart. Planshet buyurtmani o'qiyotganda ekranda "band" belgisi yonadi. O'qish muvaffaqiyatli bo'lsa ham, xato bo'lsa ham, oxirida belgi o'chishi kerak — aks holda planshet abadiy "band" bo'lib qoladi.

js
function buyurtmaniOqi(matn) {
  console.log("Planshet: band");
  try {
    const buyurtma = JSON.parse(matn);
    console.log(`Qabul: ${buyurtma.taom}`);
  } catch (xato) {
    console.log(`Rad etildi: ${xato.name}`);
  } finally {
    console.log("Planshet: bo'sh");
  }
}

buyurtmaniOqi('{"taom": "Manti"}');
buyurtmaniOqi("Manti");

Konsolda:

text
Planshet: band
Qabul: Manti
Planshet: bo'sh
Planshet: band
Rad etildi: SyntaxError
Planshet: bo'sh

Ikkala chaqiruvda ham "bo'sh" chiqdi. Xuddi shu natijaga finally siz ham erishsa bo'lardi: qatorni try oxiriga va catch oxiriga — ikki joyga — yozib. Lekin ikki nusxa — biri unutiladigan nusxa. finally "tozalash" ishini bitta joyda saqlaydi.

6.2 return bo'lsa ham

finally ning asl kuchi shu: u try dan return bilan chiqib ketilganda ham ishlaydi.

js
const menyu = { osh: 35000, manti: 30000 };

function narxniTop(taom) {
  try {
    return menyu[taom] ?? 0;
  } finally {
    console.log(`Qidiruv tugadi: ${taom}`);
  }
}

console.log(narxniTop("osh"));

Konsolda:

text
Qidiruv tugadi: osh
35000

Tartibga qarang: finally ning xabari oldin chiqdi. return menyu[taom] ?? 0 qiymatni hisoblab qo'ydi, lekin funksiyadan chiqishdan oldin finally ishladi. Shundan keyingina qiymat chaqiruvchiga yetdi.

Bu misolda catch yo'q — try...finally ham to'g'ri yozuv. U "xatoni ushlamayman, lekin tozalashni albatta qilaman" degani. try da xato chiqsa, finally ishlaydi, keyin xato yuqoriga ko'tarilishda davom etadi.

Uchta yozuv bor: try...catch, try...finally va try...catch...finally. Faqat try ning o'zi bo'lmaydi:

js
try {
  JSON.parse("{");
}
console.log("davom");
text
SyntaxError: Missing catch or finally after try

Tarjimasi: "try dan keyin catch yoki finally yo'q". Bu kodning yozuv xatosi: fayl umuman ishga tushmaydi, JSON.parse gacha ham yetib bormaydi.

6.3 Tuzoq: finally ichida return

finally har doim oxirgi so'zni aytadi — va bu xavfli bo'lishi mumkin:

js
function oqi(matn) {
  try {
    return JSON.parse(matn);
  } catch (xato) {
    console.log("catch:", xato.name);
    return null;
  } finally {
    return "finally";
  }
}

console.log(oqi('{"taom": "Osh"}'));
console.log(oqi("{"));

Konsolda:

text
finally
catch: SyntaxError
finally

Ikkala chaqiruvda ham natija — "finally". To'g'ri o'qilgan obyekt ham, catch qaytargan null ham yo'qoldi. finally dagi return oldingi return ni bosib ketadi.

Undan ham yomoni — finally dagi return xatoni ham "yo'q qiladi":

js
function yutadi() {
  try {
    JSON.parse("{");
  } finally {
    return "hammasi joyida";
  }
}

console.log(yutadi()); // hammasi joyida

catch yo'q edi, xato yuqoriga ko'tarilishi kerak edi. Lekin finally dagi return uni o'chirib yubordi — dastur "hammasi joyida" deb yolg'on gapirdi. Xato izsiz yo'qoldi.

Qoida: finally ichida return yozmang (throw, break va continue ham shunday ta'sir qiladi). finally faqat tozalash uchun: belgini o'chirish, hisoblagichni yangilash, resursni yopish. Natija try va catch da qaytariladi. Kod sifatini tekshiruvchi ESLint vositasida buning uchun alohida qoida bor — no-unsafe-finally (ESLint'ni 12-qismda o'rganamiz, hozir bilish shart emas).

Tekshirib ko'ring: function f() { try { return 1; } finally { console.log(2); } } — console.log(f()) nima chiqaradi va qaysi tartibda?

Javob

Avval 2, keyin 1. return 1 qiymatni tayyorlab qo'ydi, funksiyadan chiqishdan oldin finally ishladi va 2 ni chiqardi. Keyin 1 chaqiruvchiga qaytdi. finally da return yo'q — shuning uchun 1 o'zgarmadi.

7. Xatoni qayta tashlash

7.1 catch hammasini ushlaydi

catch xato turini tanlamaydi — try ichida chiqqan har qanday xatoni oladi. Bu yerda muammo yashirinib yotadi. Oshxonada taomlar soni hisoblanadi:

js
function taomlarSoni(matn) {
  try {
    const buyurtma = JSON.parse(matn);
    return buyurtma.taomlar.length;
  } catch {
    console.log("JSON buzilgan");
    return 0;
  }
}

console.log(taomlarSoni('{"taomlar": ["Osh", "Choy"]}'));
console.log(taomlarSoni('{"stol": 4}'));

Konsolda:

text
2
JSON buzilgan
0

Ikkinchi matn — to'g'ri JSON. Unda shunchaki taomlar yo'q: buyurtma.taomlar.length — TypeError. catch esa uni ham ushladi va "JSON buzilgan" deb yolg'on gapirdi. Otabek planshetni qayta-qayta tekshiradi — muammo esa boshqa joyda.

7.2 throw xato — "bu meniki emas"

Yechim: catch faqat o'zi tushunadigan xatoni hal qilsin. Qolganini — throw xato; bilan — yuqoriga qaytarsin. throw — "xatoni tashla" ko'rsatmasi; uni to'liq keyingi darsda o'rganamiz. Bugun faqat bitta shakli: ushlangan xatoni o'zgarishsiz qayta tashlash.

js
function taomlarSoni(matn) {
  try {
    const buyurtma = JSON.parse(matn);
    return buyurtma.taomlar.length;
  } catch (xato) {
    if (xato instanceof SyntaxError) {
      console.log("JSON buzilgan");
      return 0;
    }
    throw xato;
  }
}

console.log(taomlarSoni("{"));
console.log(taomlarSoni('{"stol": 4}'));
text
JSON buzilgan
0
TypeError: Cannot read properties of undefined (reading 'length')

Endi buzuq JSON hamon yumshoq hal qilinadi, taomlar yo'qligi esa haqiqiy xato bo'lib chiqdi — turi va matni bilan. U yuqorida kimdir ushlamasa, Uncaught bo'ladi va dasturchi uni darhol ko'radi.

Bu — xatolar bilan ishlashning eng muhim odatlaridan biri. Kutilgan xato (buzuq matn) — dasturning odatiy holati, unga javob tayyor. Kutilmagan xato (kodda taomlar yo'qligini hisobga olmaganmiz) — bug. Bug'ni yashirish uni tuzatishni imkonsiz qiladi. Shuning uchun begona xato qayta tashlanadi.

Bitta tuzoq: instanceof SyntaxError ham to'liq aniq emas. try ichidagi boshqa kod ham SyntaxError tashlashi mumkin (masalan, Stack trace darsidagi noto'g'ri selektor). Eng ishonchli yo'l — try ichida faqat bitta xavfli chaqiruvni qoldirish. Xatolarni o'z turlari bilan ajratish — O'z xato klasslaringiz darsida.

Tekshirib ko'ring: catch (xato) { throw xato; } — catch siz yozilgan koddan nimasi bilan farq qiladi?

Javob

Deyarli hech nimasi bilan: xato o'zgarishsiz yuqoriga ketadi. Bunday catch ma'nosiz — uni olib tashlang. Qayta tashlash faqat shart bilan foydali: bir xil xatolarni hal qilib, qolganini qaytarish.

8. try nimani ushlamaydi

try faqat o'z bloki ishlayotgan paytda chiqqan xatoni ushlaydi. Ikki muhim istisno bor.

Keyinroq ishlaydigan kod. Stack trace darsida setTimeout bilan rejalashtirilgan funksiyani ko'rgansiz: uni keyinroq brauzer yoki Node chaqiradi. O'sha paytda try allaqachon tugagan bo'ladi:

js
try {
  setTimeout(() => {
    JSON.parse("{");
  }, 0);
} catch {
  console.log("ushlandi");
}
console.log("try tugadi");
text
try tugadi
SyntaxError: Expected property name or '}' in JSON at position 1 (line 1 column 2)

"ushlandi" chiqmadi. try faqat taymerni qo'ydi va tugadi — xato 0 ms dan keyin, butunlay boshqa chaqiruvda chiqdi. Hodisa tinglovchilari ham shunday: tinglovchini ulaydigan addEventListener atrofidagi try tinglovchi ichidagi xatoni ushlamaydi. try ni funksiyaning ichiga qo'yish kerak. Tarmoq so'rovlari va Promise (natijasi keyinroq keladigan ish — 11-qismda o'rganamiz) dagi xatolar uchun boshqa yo'l bor — uni 11-qismda o'rganamiz.

Faylning yozuv xatosi. Fayl ichida SyntaxError bo'lsa (yopilmagan qavs, Missing catch or finally), kod umuman ishga tushmaydi — try ham. try faqat ishlash paytidagi xatolarni ushlaydi: JSON.parse kabi funksiyalar matnni o'qiyotganda tashlaydiganlarini.

9. Brauzerda: oshxona ekrani

Endi brauzerda. Planshetdan kelgan matn maydonda turibdi. «Qabul qilish» bosilganda u o'qiladi, xato bo'lsa — tushunarli xabar chiqadi. Pastdagi hisoblagich finally da yangilanadi:

html
<style>
  body { margin: 1rem; font: 1rem/1.5 system-ui, sans-serif; }
  textarea, button { font: inherit; }
  textarea { width: 100%; box-sizing: border-box; }
  button { min-height: 2.75rem; }
  #xabar { min-height: 1.5em; font-weight: bold; }
</style>
<label for="buyurtma">Planshetdan kelgan matn</label>
<textarea id="buyurtma" rows="3">
{"stol": 4, "taomlar": ["Osh", "Ko'k choy"]}</textarea>
<button type="button" id="qabul">Qabul qilish</button>
<p id="xabar" aria-live="polite"></p>
<p id="hisob"></p>
<script>
  const maydon = document.getElementById("buyurtma");
  const xabar = document.getElementById("xabar");
  const hisob = document.getElementById("hisob");
  let tekshiruvlar = 0;

  function qabulQil() {
    try {
      const b = JSON.parse(maydon.value);
      xabar.textContent = `${b.stol}-stol: ${b.taomlar.join(", ")}`;
    } catch (xato) {
      xabar.textContent = "Matn buzilgan. Qayta yuborishni so'rang.";
      console.log(`${xato.name}: ${xato.message}`);
    } finally {
      tekshiruvlar++;
      hisob.textContent = `Tekshiruvlar: ${tekshiruvlar}`;
    }
  }

  const tugma = document.getElementById("qabul");
  tugma.addEventListener("click", qabulQil);
  qabulQil();
</script>

Sahifa ochilganda — "4-stol: Osh, Ko'k choy", "Tekshiruvlar: 1". Sinab ko'ring:

  • Matn oxiridagi } ni o'chiring va bosing. Ekranda — o'zbekcha xabar, konsolda esa dasturchi uchun asl matn: SyntaxError: Expected ',' or '}' after property value in JSON at position 43 (line 1 column 44). Hisoblagich oshdi — finally ishladi.
  • Matnni tiklang, «Qabul qilish» — yana to'g'ri natija. Sahifa bir marta ham "o'lmadi".
  • Endi "taomlar" so'zini "taom" ga o'zgartiring. Ekranda yana "Matn buzilgan", konsolda esa TypeError: Cannot read properties of undefined (reading 'join'). Matn buzilmagan — tuzilma boshqa! «Xatoni qayta tashlash» bo'limidagi muammo aynan shu. Uni keyingi darsda throw bilan to'g'ri hal qilamiz.

aria-live="polite" — ekran o'quvchisi uchun: xabar matni o'zgarsa, NVDA uni o'qib beradi (Dinamik UI'da qulaylik). Malika xato xabarini ko'rmasdan ham eshitadi.

10. Ko'p uchraydigan xatolar

10.1 Bo'sh catch — xatoni yutib yuborish

js
try {
  chekniHisobla(taomlar);
} catch {}

Xato yo'qoldi, hech kim bilmaydi. Natija — noto'g'ri ishlaydigan, lekin "jim" dastur. Debugging metodologiyasi darsidagi eng og'ir xatolar aynan shunday paydo bo'ladi. Tuzatish: catch da kamida xabar bering yoki xatoni qayta tashlang. Faqat aniq sababini bilsangiz (jsonmi kabi) xatoni e'tiborsiz qoldiring.

10.2 Butun funksiya try ichida

try ga 30 qator kod solinsa, catch qaysi qatordagi xatoni ushlaganini bilmaydi — «catch hammasini ushlaydi» bo'limidagi muammo. Tuzatish: try ichida faqat xato kutilgan qator(lar). Natija kerak bo'lsa — let ni blokdan oldin e'lon qiling.

10.3 try dagi const ni tashqarida ishlatish

ReferenceError: buyurtma is not defined — «try ichidagi o'zgaruvchi» bo'limi. Tuzatish: let buyurtma; blokdan oldin.

10.4 finally da return

Natija va xato yo'qoladi. Tuzatish: finally — faqat tozalash.

10.5 try taymer yoki tinglovchi tashqarisida

try { tugma.addEventListener("click", ishla); } catch {} — ishla ichidagi xato ushlanmaydi. Tuzatish: try ni ishla funksiyasining ichiga qo'ying.

11. Mashqlar

1-mashq (oson): Xavfsiz o'qish

xavfsizOqi(matn) funksiyasini yozing: matn to'g'ri JSON bo'lsa — o'qilgan qiymatni, aks holda null qaytarsin. Xato obyekti kerak emas — optional catch binding'dan foydalaning («catch parametrisiz» bo'limi). Uchta matn bilan sinang: '{"taom": "Osh"}', "Osh", "".

Yechim
js
function xavfsizOqi(matn) {
  try {
    return JSON.parse(matn);
  } catch {
    return null;
  }
}

console.log(xavfsizOqi('{"taom": "Osh"}')); // { taom: 'Osh' }
console.log(xavfsizOqi("Osh")); // null
console.log(xavfsizOqi("")); // null

Bitta kamchiligi bor: xavfsizOqi("null") ham null qaytaradi — matn to'g'ri bo'lsa ham. Chaqiruvchi "buzuq matn" va "matnda null" ni ajrata olmaydi. Ko'p hollarda bu muhim emas. Muhim bo'lsa — natijani obyekt qilib qaytarish usulini Xato strategiyasi darsida ko'ramiz.

2-mashq (o'rta): Kunlik hisob

Kun davomida planshetdan kelgan matnlar massivda. Har birini o'qing: to'g'rilarining summa sini qo'shing, buzuqlarini sanang. Bitta buzuq matn qolganlarini to'xtatmasin. Ishora: try ni sikl ichiga qo'ying; for...of dan foydalaning (for...of).

js
const kelganlar = [
  '{"stol": 1, "summa": 70000}',
  '{"stol": 2, "summa": 33000',
  '{"stol": 3, "summa": 58000}',
  "",
];
Yechim
js
const kelganlar = [
  '{"stol": 1, "summa": 70000}',
  '{"stol": 2, "summa": 33000',
  '{"stol": 3, "summa": 58000}',
  "",
];

let tushum = 0;
let buzuqlar = 0;

for (const matn of kelganlar) {
  try {
    tushum += JSON.parse(matn).summa;
  } catch {
    buzuqlar++;
  }
}

console.log(`Tushum: ${tushum}, buzuq: ${buzuqlar}`);

Konsolda:

text
Tushum: 128000, buzuq: 2

try sikl ichida — har matn alohida "to'r" ga ega. Agar try sikl tashqarisida bo'lganida, ikkinchi matn butun siklni to'xtatar va uchinchi buyurtma umuman sanalmas edi.

3-mashq (qiyin): Faqat o'zimizniki

stolniOl(matn) funksiyasini yozing. U buyurtma matnidan stol raqamini qaytarsin. Talablar:

  1. Buzuq JSON — "Buyurtma buzilgan" ni konsolga chiqarib, null qaytarsin.
  2. Boshqa har qanday xato — qayta tashlansin.
  3. finally da "tekshirildi" chiqsin.

'{"stol": 4}', "{" va "null" bilan sinang. Uchinchisi nima beradi va nega?

Yechim
js
function stolniOl(matn) {
  try {
    return JSON.parse(matn).stol;
  } catch (xato) {
    if (xato instanceof SyntaxError) {
      console.log("Buyurtma buzilgan");
      return null;
    }
    throw xato;
  } finally {
    console.log("tekshirildi");
  }
}

console.log(stolniOl('{"stol": 4}'));
console.log(stolniOl("{"));
console.log(stolniOl("null"));
text
tekshirildi
4
Buyurtma buzilgan
tekshirildi
null
tekshirildi
TypeError: Cannot read properties of null (reading 'stol')

Uchinchi chaqiruvda "null" — to'g'ri JSON, parse null qaytardi. Keyin null.stol — TypeError. U SyntaxError emas, shuning uchun qayta tashlandi. E'tibor bering: finally baribir ishladi — "tekshirildi" xatodan oldin chiqdi. Qayta tashlangan xato finally dan keyin yuqoriga ketdi.

"null" holatini bug deb hisoblash to'g'rimi? Bu — qaror: tashqaridan kelgan matnda null ham bo'lishi mumkin, demak uni ham "noto'g'ri buyurtma" sifatida yumshoq hal qilish kerak. Bunday tekshiruvni throw bilan qanday yozish — keyingi darsda.

4-mashq: Vazifalar qadami — JSON import

JSON chuqur darsida eksport formatini tayyorladik: { "versiya": 1, "vazifalar": [...] }. Prototype pollution darsida esa kurs/mashqlar/10/35-himoya/xavfsiz-import.js da importning xavfsiz yadrosini yozdingiz: XAVFLI_KALITLAR, oddiyObyektmi, xavfsizBirlashtir, vazifaniOl va paketniOqi. Bugun ularni ilovaga ulaymiz: foydalanuvchi eksport matnini maydonga qo'yadi, tugmani bosadi — ro'yxat almashadi.

Bu — yangi imkoniyat: git switch -c feature/import. Branch'ni xatolar moduli oxirigacha — Xato strategiyasi darsigacha — ochiq qoldiramiz.

Talablar:

  1. index.html da eksport maydonidan keyin: Import sarlavhasi, yorliqli id="import-matn" maydon, id="import-tugma" tugma va xato uchun <p id="import-xato" class="xato" aria-live="polite">.
  2. vazifalar.js ning "3. Hisoblovchi funksiyalar" bo'limiga 35-himoya dagi nomlar (o'zgarishsiz).
  3. Tugma bosilganda: paketniOqi try ichida. Buzuq JSON — #import-xato da o'zbekcha xabar, maydonga fokus va aria-invalid="true"; ro'yxat o'zgarmasin. Muvaffaqiyatda — ro'yxat almashsin, filtr «Hammasi» bo'lsin, #elon da "N ta vazifa import qilindi."
  4. Yangi royxat VazifalarRoyxati.dan(...) bilan yasalsin. Diqqat: render royxatga obuna orqali bog'langan (Dizayn naqshlari).
Yechim

index.html — #eksport-matn dan keyin:

html
<h2>Import</h2>
<label for="import-matn">Eksport qilingan JSON matni</label>
<textarea id="import-matn" rows="6"></textarea>
<button type="button" id="import-tugma">Import qilish</button>
<p id="import-xato" class="xato" aria-live="polite"></p>

.xato stili 09-qismdan bor — xabar qizil chiqadi.

vazifalar.js — "2. Sahifa elementlari" ga:

js
const importMatn = document.getElementById("import-matn");
const importTugma = document.getElementById("import-tugma");
const importXato = document.getElementById("import-xato");

"3. Hisoblovchi funksiyalar" ga — 35-himoya/xavfsiz-import.js dagi XAVFLI_KALITLAR, VAZIFA_MAYDONLARI, oddiyObyektmi, xavfsizBirlashtir, vazifaniOl va paketniOqi (o'zgarishsiz ko'chiring). Ularning ostiga — uch yordamchi:

js
function importXatosiniKorsat(xabar) {
  importXato.textContent = xabar;
  importMatn.setAttribute("aria-invalid", "true");
  importMatn.focus();
}

function importXatosiniTozala() {
  importXato.textContent = "";
  importMatn.removeAttribute("aria-invalid");
}

function royxatniAlmashtir(malumotlar) {
  holat.vazifalar = VazifalarRoyxati.dan(malumotlar);
  holat.vazifalar.obuna(render);
  holat.filtr = "hammasi";
  holat.tahrirId = null;
  render();
}

"7. Hodisalar" ga, eksport tinglovchisidan keyin:

js
importTugma.addEventListener("click", () => {
  importXatosiniTozala();
  elonQil("");
  let malumotlar;
  try {
    malumotlar = paketniOqi(importMatn.value);
  } catch {
    importXatosiniKorsat("Matn JSON emas. Eksportni to'liq oling.");
    return;
  }
  royxatniAlmashtir(malumotlar);
  elonQil(`${malumotlar.length} ta vazifa import qilindi.`);
});

Nimaga e'tibor bering:

  • try ichida bitta qator. Natija let malumotlar ga yoziladi, u blokdan oldin e'lon qilingan («try ichidagi o'zgaruvchi» bo'limi). Xato bo'lsa — return, va malumotlar dan hech kim foydalanmaydi.
  • Holat xavfli qadamdan keyin o'zgaradi. Avval o'qish, keyin royxatniAlmashtir. O'qish yiqilsa, eski ro'yxat joyida qoladi — «Bajarilish tartibi» bo'limidagi qoida.
  • obuna(render) qayta. VazifalarRoyxati.dan yangi royxat yasaydi. Eski royxatning tinglovchilari unga ko'chmaydi. Bu qatorni unutsangiz, import ishlaydi, lekin keyingi bosishlar ekranni yangilamaydi — jim xato. id lar esa dan ichida hisoblanadi: yangi vazifa importdagi eng katta id dan keyingisini oladi.
  • Ikki xabar, ikki joy. Xato — #import-xato da (maydon yonida, aria-live), muvaffaqiyat — umumiy #elon da.

paketniOqi endi try ichida qanday ishlashini Node'da tekshiring. Yadroni xavfsiz-import.js dan import qilmaymiz (modullar hali o'tilmagan) — qisqartirilgan nusxasi bilan:

js
function paketniOqi(matn) {
  const paket = JSON.parse(matn);
  if (paket.versiya !== 1 || !Array.isArray(paket.vazifalar)) {
    return [];
  }
  return paket.vazifalar;
}

const matnlar = [
  '{"versiya": 1, "vazifalar": [{"id": 5, "matn": "Choy"}]}',
  '{"versiya": 1, "vazifalar": [}',
  '{"versiya": 2, "vazifalar": [{"id": 5, "matn": "Choy"}]}',
];

for (const matn of matnlar) {
  try {
    console.log("O'qildi:", paketniOqi(matn).length);
  } catch (xato) {
    console.log("Ushlandi:", xato.name);
  }
}

Konsolda:

text
O'qildi: 1
Ushlandi: SyntaxError
O'qildi: 0

Brauzerda sinab ko'ring (bu ssenariy Chrome 154 da qadamma-qadam tekshirilgan):

  • «JSON qilib chiqarish» → matnni nusxalang. "Non olish" ni belgilang. Nusxani import maydoniga qo'yib, «Import qilish» — ro'yxat eksport paytidagi holatiga qaytdi, "3 ta vazifa import qilindi.".
  • Import qilingan vazifani belgilang — ekran yangilanadi (obuna ishlayapti). Yangi vazifa qo'shing — u keyingi id ni oladi.
  • {"versiya": 1, "vazifalar": [} — qizil xabar, fokus maydonda, ro'yxat o'zgarmadi. Bo'sh maydon — xuddi shunday.
  • __proto__ kalitli paket (35-himoya dagi proto matni) — vazifalar o'qildi, DevTools konsolida ({}).admin — undefined.

Va uchta ochiq muammo — ularni ham sinang, lekin hozir tuzatmang:

  • {"versiya": 2, ...} — xato yo'q, ro'yxat bo'shab qoldi, "0 ta vazifa import qilindi.". paketniOqi noto'g'ri versiyada jimgina [] qaytaradi — bo'sh eksport bilan farqi yo'q.
  • null — to'g'ri JSON, lekin xabar "Matn JSON emas". xavfsizBirlashtir null ni aylanmoqchi bo'lib TypeError tashladi, parametrsiz catch esa uni JSON xatosi deb o'yladi — «catch hammasini ushlaydi» bo'limidagi tuzoq.
  • "bajarildi": "ha" — satr qabul qilindi va vazifa chizilgan ko'rinadi. Turlar tekshirilmayapti.

Birinchi ikkitasini keyingi darsda throw bilan, uchinchisini Xato strategiyasi darsida hal qilamiz.

Commit:

bash
git add index.html assets/js/vazifalar.js
git commit -m "Vazifalar: JSON importni qo'sh (try/catch)"

12. Real ishda

  • Tashqi ma'lumot chegarasi. JSON.parse, fayl o'qish, foydalanuvchi kiritgan matn, serverdan kelgan javob — dastur "tashqi dunyo" bilan uchrashadigan har joyda try...catch turadi. Ichki, o'zingiz yozgan kodda esa u kamdan-kam kerak.
  • Server ilovalari. Express va NestJS (server yozish freymvorklari — kursda keyinroq) har so'rovni o'z try ichida ishlatadi: bitta so'rovdagi xato serverni yiqitmaydi, foydalanuvchi "500" (serverdagi xato kodi) javobini oladi, qolganlar ishlashda davom etadi. Bu — «Kunlik hisob» mashqidagi "sikl ichidagi try" ning kattalashgan shakli.
  • finally — tozalash. "Yuklanmoqda..." belgisini o'chirish, tugmani qayta faollashtirish, faylni yopish. Resursni avtomatik yopishning yangi usuli — using — shu modulning oxirgi darsida.
  • Intervyu. "finally qachon ishlamaydi?" (javob: deyarli har doim ishlaydi; finally dagi return natijani bosib ketadi), "try...catch asinxron xatoni ushlaydimi?", "bo'sh catch nega yomon?" — tez-tez so'raladi.

Xulosa

  • try — xavfli kod, catch (xato) — xato chiqqanda ishlaydi, finally — har doim. Faqat try ning o'zi bo'lmaydi.
  • Xato chiqqan zahoti try ning qolgan qatorlari tashlanadi; oldingi qatorlar bekor qilinmaydi.
  • Xato chaqiruvlar steki bo'ylab birinchi catch gacha ko'tariladi — try ni javob bera oladigan joyga qo'ying.
  • try ichidagi const/let tashqarida yo'q; try ni kichik saqlang, natija uchun let ni oldinroq e'lon qiling.
  • catch { } — xato obyekti kerak bo'lmaganda; finally da return yozmang.
  • catch hamma xatoni ushlaydi: o'zingiznikini hal qiling, begonasini throw xato bilan qaytaring.

Keyingi dars: throw va Error obyekti — o'zingiz xato tashlashni, message, name, stack va sabablar zanjiri cause ni o'rganamiz va importdagi jim xatolarni yo'qotamiz.

Manbalar

  • MDN: "try...catch", "Error" — developer.mozilla.org
  • ECMAScript 2025 Language Specification: "The try Statement" — tc39.es/ecma262
  • web-features 3.40 (npm paketi): optional-catch-binding — Baseline 2020-01-15, keng qo'llab-quvvatlanadi 2022-07-15
  • ESLint: "no-unsafe-finally" — eslint.org/docs/latest/rules
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript try...catch...finally: xatoni ushlash va dasturni yiqitmaslik — IlmHamroh