Xatoliklarni boshqarish

Maxsus xatoliklar, Error'ni kengaytirish

Ilovamiz murakkablashgani sari, bizga o'z maxsus xato turlari kerak bo'ladi. Masalan, tarmoq operatsiyasida HttpError, ma'lumotlarni tekshirishda ValidationError va hokazo. Bunday xatolar HTTP status kodi yoki noto'g'ri maydon nomi kabi qo'shimcha ma'lumotlarni saqlashi kerak. JavaScriptda o'z xatolarimizni yaratish uchun ularni Errordan meros olib (extends) yasaymiz.

Nega Error'ni kengaytiramiz?

Faraz qilaylik, bizda readUser(json) funksiyasi bor โ€” u JSON matnini o'qiydi. To'liq ishlashi uchun foydalanuvchida name va age maydonlari bo'lishi kerak. Agar ular bo'lmasa, bu SyntaxError emas โ€” chunki JSON o'zi to'g'ri. Bu boshqa turdagi xato. Uni ValidationError deb ataymiz.

Bizning xato klassimiz standart xatolar kabi message, name, stack xususiyatlarini qo'llab-quvvatlashi kerak. Buning uchun eng oson yo'l โ€” o'rnatilgan Error klassidan meros olish.

Error'dan extends qilish

Error klassini kengaytiramiz. Errorning tuzilishi taxminan shunday (soddalashtirilgan):

// JavaScript'ning ichki o'rnatilgan Error klassi (taxminan)
class Error {
  constructor(message) {
    this.message = message;
    this.name = "Error"; // o'rnatilgan xatolar uchun turli nomlar
    this.stack = /* chaqiruvlar steki */;
  }
}

Endi undan ValidationErrorni meros olamiz:

validation-error.js
// natija shu yerda chiqadi

Muhim jihatlar:

instanceof bilan tekshirish

Endi readUserni yozamiz va ValidationError tashlaymiz. Xatoni ushlaganda uning aynan qaysi tur ekanligini instanceof orqali aniqlaymiz:

instanceof.js
// natija shu yerda chiqadi

instanceof nafaqat aynan klassni, balki uning barcha ota-klasslarini ham tan oladi. Ya'ni ValidationError ham Errorning avlodi bo'lgani uchun:

instanceof-hierarchy.js
// natija shu yerda chiqadi
Xatoni tekshirishda odatda aniq (ValidationError) turdan umumiy (Error) tur tomon boradigan tartibda instanceof ishlatiladi. Aks holda umumiy tur birinchi bo'lib "ushlab olib", aniq shohobchalarga yetib bormaydi.

Xatoliklar ierarxiyasi

Ilova o'sgani sari, xatolar ham ko'payadi. Masalan, PropertyRequiredError โ€” aynan qaysi maydon yetishmayotganini bildiradigan aniqroq xato. U ValidationErrordan meros olishi mumkin. Shunday qilib ierarxiya (daraxt) hosil bo'ladi:

Error
 โ””โ”€โ”€ ValidationError
      โ””โ”€โ”€ PropertyRequiredError
ierarxiya.js
// natija shu yerda chiqadi

E'tibor bering: catchda biz instanceof ValidationError deb tekshirsak, u PropertyRequiredErrorni ham qamrab oladi โ€” chunki u ValidationErrorning avlodi. Bu ierarxiyaning kuchi: bitta tekshiruv butun bir tarmoqni ushlaydi.

this.nameni har bir konstruktorda qo'lda yozib chiqish zerikarli. Buning o'rniga asosiy maxsus xato klassi yaratib, unda this.name = this.constructor.name deb yozish mumkin โ€” shunda avlodlar nomni avtomatik oladi.
auto-name.js
// natija shu yerda chiqadi

Xatolarni o'rash (Wrapping exceptions)

Ierarxiya foydali, lekin muammosi bor. Faraz qilaylik, readUserni chaqiruvchi kod turli-tuman xatolar (ValidationError, SyntaxError, kelajakda yana boshqalar) haqida qayg'urishi kerak. Har safar yangi xato turi qo'shilganda, uni chaqiruvchi hamma joyda tekshiruvni yangilashimiz kerak bo'ladi. Bu noqulay.

Yechim โ€” wrapping (o'rash) texnikasi:

  1. Barcha "quyi darajali" xatolarni umumlashtiruvchi bitta klass yaratamiz โ€” masalan, ReadError;
  2. readUser ichidagi try...catch quyi darajali xatolarni (ValidationError, SyntaxError) ushlaydi;
  3. So'ng ularni bitta umumiy ReadError ichiga "o'rab", uni tashlaydi. Asl xato esa cause (sabab) xususiyatida saqlanadi.

Endi chaqiruvchi kod faqat bitta ReadErrorni tekshirsa kifoya. Tafsilot kerak bo'lsa, err.causeni ochib ko'radi:

wrapping.js
// natija shu yerda chiqadi

Endi ikkinchi holatni โ€” validatsiya xatosini โ€” sinab ko'ring: JSON to'g'ri, lekin maydon yo'q:

wrapping-2.js
// natija shu yerda chiqadi
Zamonaviy JavaScriptda Error konstruktori ikkinchi argument sifatida { cause } obyektini qabul qiladi: new Error("...", { cause: err }). Bu causeni saqlashning standart usuli. Yuqorida biz uni sodda ko'rinishda qo'lda o'rnatdik.
Wrapping'ning asosiy foydasi โ€” abstraksiya darajasi. Chaqiruvchi kod pastki qavatdagi barcha xato turlarini bilishi shart emas; u faqat "o'qishda xato bo'ldi" (ReadError) degan umumiy ma'lumotni oladi, kerak bo'lganda esa cause orqali chuqurroq kirib ko'radi.

Xulosa