Xatoliklarni boshqarish, try...catch
Kod qanchalik yaxshi yozilmasin, xatolar muqarrar. Sababi ko'p: bizning xatomiz, kutilmagan foydalanuvchi kiritmasi, noto'g'ri server javobi va yana minglab holatlar. Odatda xato bo'lganda skript "o'ladi" (to'xtaydi) va konsolga xato chiqadi. Ammo try...catch sintaksisi bunday xatolarni "ushlab olib", skriptni to'xtatmasdan, oqilona harakat qilishga imkon beradi.
try...catch sintaksisi
try...catch konstruksiyasi ikki asosiy blokdan iborat: try va so'ng catch:
try {
// kod...
} catch (err) {
// xatoni qayta ishlash
}
U quyidagicha ishlaydi:
- Avval
try {...}ichidagi kod bajariladi; - Agar xato bo'lmasa,
catch(err)bloki e'tiborsiz qoldiriladi: bajarilishtryoxiriga yetadi vacatchdan sakrab o'tadi; - Agar xato yuz bersa,
trybajarilishi o'sha joyda to'xtaydi va boshqaruvcatch(err)blokining boshiga o'tadi.erro'zgaruvchisi (istalgan nom bo'lishi mumkin) nima yuz berganini tavsiflovchi xato obyektini saqlaydi.
Shunday qilib, try {...} ichidagi xato skriptni o'ldirmaydi โ bizda uni catch ichida qayta ishlash imkoni bor.
Xatosiz misol โ catch umuman ishlamaydi:
Endi xatoli misol:
E'tibor bering: xatodan keyingi (2) qatori umuman bajarilmadi. JavaScript darhol catchga sakradi, skriptni butunlay to'xtatmadi.
try...catch faqat runtime xatolar uchun ishlaydi
try...catch ishlashi uchun kod bajarilayotgan ("runnable") bo'lishi shart. Ya'ni u to'g'ri JavaScript bo'lishi kerak.
{{{{), try...catch yordam bermaydi. JavaScript dvigateli avval kodni o'qiydi (parse qiladi), so'ng ishga tushiradi. Parse bosqichida topilgan xatolar "parse-time" xatolari deb ataladi va ular try...catch ichida ushlanmaydi โ chunki dvigatel bu kodni tushunmaydi.Demak, try...catch faqat to'g'ri kodda yuz beradigan xatolarni ushlaydi. Bunday xatolar "runtime errors" (ishga tushish paytidagi xatolar) yoki ba'zan "exceptions" (istisnolar) deb ataladi.
try...catch sinxron ishlaydi
Agar xato "rejalashtirilgan" kodda (masalan, setTimeout ichida) yuz bersa, uni try...catch ushlay olmaydi:
try {
setTimeout(function() {
noSuchVariable; // skript bu yerda to'xtaydi
}, 1000);
} catch (err) {
console.log("ushlanmaydi!");
}
Chunki setTimeout ichidagi funksiya keyinroq, dvigatel try...catch konstruksiyasidan allaqachon chiqib ketgandan so'ng bajariladi. Xatoni ushlash uchun try...catch aynan o'sha funksiya ichida bo'lishi kerak:
setTimeout(function() {
try {
noSuchVariable; // endi try...catch xatoni ushlaydi!
} catch (err) {
console.log("xato bu yerda ushlandi");
}
}, 1000);
async/await) bilan ishlaganda ham xuddi shu qoida amal qiladi, u haqda keyingi boblarda batafsil to'xtalamiz.Error obyekti
Xato yuz berganda, JavaScript uni tavsiflovchi obyekt yaratadi. So'ng bu obyekt catchga argument sifatida uzatiladi:
try {
// ...
} catch (err) { // <-- "xato obyekti", boshqa nom ham berilsa bo'ladi
// ...
}
O'rnatilgan xatolar uchun error obyekti ikkita asosiy xususiyatga ega:
nameโ xato nomi. Masalan, aniqlanmagan o'zgaruvchi uchun bu"ReferenceError"bo'ladi;messageโ xato tafsiloti haqidagi matnli xabar.
Ko'p muhitlarda yana bir standart bo'lmagan, lekin keng qo'llab-quvvatlanadigan xususiyat bor:
stackโ xatoga olib kelgan chaqiruvlar ketma-ketligi (call stack). Debugging uchun juda foydali.
console.log(err) yoki matn sifatida console.log(err + "") โ u name: message ko'rinishida chiqadi.Amaliy misol: JSON.parse xatosi
Amalda try...catch eng ko'p ma'lumotni o'qiyotganda kerak bo'ladi. Masalan, serverdan yoki foydalanuvchidan kelgan JSON matnini JSON.parse bilan tahlil qilamiz. Agar matn buzuq bo'lsa, JSON.parse xato tashlaydi va skript to'xtaydi. Buni oldini olamiz:
Endi to'g'ri JSON bilan solishtiring โ catch ishlamaydi:
throw operatori โ o'z xatolaringizni tashlash
Yuqoridagi JSON misolida qiziq holat bor: JSON matni to'g'ri bo'lishi mumkin, lekin unda kerakli maydon (masalan, name) yo'q bo'lishi mumkin. Bunda JSON.parse xato tashlamaydi โ chunki JSON o'zi haqiqiy. Lekin biz uchun bu xato. Bunday holatlarni o'zimiz aniqlab, o'z xatomizni tashlashimiz kerak. Buning uchun throw operatori ishlatiladi.
throw <xato obyekti>
Texnik jihatdan istalgan qiymatni throw bilan tashlash mumkin (son, satr), lekin eng yaxshi amaliyot โ Error yoki uning avlodlaridan foydalanish. JavaScriptda standart o'rnatilgan xato konstruktorlari bor:
Errorโ umumiy xato;SyntaxErrorโ sintaktik xato (masalan, buzuq JSON);ReferenceErrorโ aniqlanmagan o'zgaruvchiga murojaat;TypeErrorโ noto'g'ri turdagi qiymat bilan amal;RangeErrorโ ruxsat etilgan diapazondan chiqib ketish.
Ularning barchasi bir xil chaqiriladi โ argument sifatida message beriladi:
Endi throw yordamida to'liq misolni ko'ramiz โ JSON to'g'ri, lekin name maydoni yo'q:
Diqqat qiling: catch bloki ikkala turdagi xatoni ham ushlaydi โ JSON.parse tashlaganini ham, biz throw qilganimizni ham. Bu juda qulay: bitta joyda barcha xatolar bilan ishlaymiz.
Qayta tashlash (rethrowing)
Yuqoridagi catch hamma xatolarni ushlaydi. Lekin bu muammo tug'diradi: catch(err) faqat biz kutgan xatoni (to'liq emas ma'lumot) qayta ishlashi kerak edi. Agar u yerda kutilmagan boshqa xato bo'lsa-chi? Masalan, biz kod yozishda xato qilib, aniqlanmagan o'zgaruvchini chaqirsak โ bu ReferenceErrorni ham "JSON Xato" deb ko'rsatib qo'yamiz, bu esa debugging'ni qiyinlashtiradi.
Yechim "qayta tashlash" (rethrowing) texnikasi: catch faqat o'zi biladigan xatolarni qayta ishlaydi, qolganlarini esa throw err orqali "yuqoriga" uzatadi.
catchhamma xatoni oladi;catch (err) {...}blokida bizerrni tekshiramiz โ bu qanday xato?;- Agar biz uni qanday qayta ishlashni bilmasak โ
throw errqilamiz.
Odatda tur instanceof operatori bilan tekshiriladi:
Yuqorida blabla() mavjud emas, shuning uchun ReferenceError yuz beradi. U SyntaxError emas, shuning uchun throw err ishga tushadi va xato "yuqoriga" tashlanadi. Agar bu try...catch tashqarisida yana bir try...catch bo'lsa, xatoni o'sha ushlaydi. Aks holda skript to'xtaydi.
catch faqat o'zi tushunadigan xatolar bilan ishlashi, boshqalarini esa qayta tashlashi kerak. Bu "begona" xatolarni yashirib qo'yishning oldini oladi.try...catch...finally
try...catchning to'liq shakli yana bir ixtiyoriy blokka ega โ finally. Agar u mavjud bo'lsa, u har doim bajariladi:
trymuvaffaqiyatli tugaganda โfinallyishlaydi;tryda xato bo'lganda vacatchishlaganda ham โfinallyishlaydi.
try {
// ... kodni bajarishga urinamiz ...
} catch (err) {
// ... xatolarni qayta ishlaymiz ...
} finally {
// ... har doim bajariladi ...
}
finally odatda biror ishni boshlaganimizda va uni xato bo'ladimi-yo'qmi, baribir yakunlashimiz kerak bo'lganda ishlatiladi โ masalan, o'lchashni to'xtatish, ulanishni yopish, yuklanish indikatorini o'chirish.
finally hatto try yoki catch ichida return bo'lsa ham ishlaydi! return qiymatni tayyorlaydi, lekin funksiya haqiqatan chiqishidan oldin finally bajariladi.catch blokini tushirib qoldirsa ham bo'ladi: try...finally shaklida. Bunda xatolar ushlanmaydi (yuqoriga tashlanadi), lekin finally baribir ishlaydi โ masalan, tozalash uchun.Global catch (qisqacha)
Agar try...catch tashqarisida o'lik xato yuz bersa, skript "o'ladi". Ba'zan bunday ushlanmagan xatolarni bir joyda umumiy tarzda ushlash foydali โ masalan, jurnalga yozish (log) yoki foydalanuvchiga xabar berish uchun.
Muhitga qarab mexanizm boshqacha:
- Brauzer:
window.onerror = function(message, url, line, col, error) {...}โ global xato ishlovchisi; - Node.js:
process.on("uncaughtException", function(err) {...})โ ushlanmagan istisnolar uchun.
Xulosa
try...catchruntime xatolarni ushlaydi:tryda xato bo'lsa, boshqaruvcatchga o'tadi va skript to'xtamaydi;- U sinxron ishlaydi va parse-time (sintaktik) xatolarni ushlamaydi;
- Error obyektining asosiy xususiyatlari:
name,message,stack; throwoperatori bilan o'z xatolarimizni tashlaymiz โ eng yaxshisinew Error(...)yoki uning turlari;- Rethrowing:
catchfaqat o'zi biladigan xatolarni ishlatadi, boshqalarinithrow errorqali qayta tashlaydi; finallybloki har doim (return bo'lsa ham) ishlaydi โ tozalash uchun ideal;- Ushlanmagan xatolar uchun global ishlovchi (brauzerda
window.onerror, Node.js'dauncaughtException) mavjud.