Biz biron bir narsani ishlab chiqsak, koâpincha xatolarimizni aniqlab olish uchun oâz xato klasslarimizga ehtiyoj sezamiz. Tarmoq operatsiyalaridagi xatolar uchun bizga HttpError, maâlumotlar bazasi operatsiyalari uchun DbError, qidiruv operatsiyalari uchun NotFoundError va boshqalar kerak boâlishi mumkin.
Bizning xatolarimiz message, name va stack kabi asosiy xato xususiyatlarini qoâllab-quvvatlashi kerak. Ammo ular oâzlarining boshqa xususiyatlariga ham ega boâlishi mumkin, masalan, HttpError obyektlari 404 yoki 403 yoki 500 kabi qiymatga ega boâlgan statusCode xususiyatiga ega boâlishi mumkin.
JavaScript har qanday argument bilan throw dan foydalanishga imkon beradi, shuning uchun texnik jihatdan bizning maxsus xato klasslarimiz Error dan meros boâlib oâtishi shart emas. Ammo biz meros qilib olsak, xato obyektlarini aniqlash uchun obj instanceof Error dan foydalanish mumkin boâladi. Shunday qilib, undan meros olish yaxshiroqdir.
Ilovamizni tuzish jarayonida oâz xatolarimiz tabiiy ravishda iyerarxiyani shakllantiradi, masalan, HttpTimeoutError HttpError dan meros boâlib oâtishi mumkin va hokazo.
Error kengaytmasi
Masalan, JSON-ni foydalanuvchi maâlumotlari bilan oâqishi kerak boâlgan readUser(json) funktsiyasini koârib chiqamiz.
Yaroqli json koârinishi mumkin boâlgan misol:
let json = `{ "name": "John", "age": 30 }`;
Ichki sifatida biz JSON.parse dan foydalanamiz. Agar u notoâgâri shakllangan json qabul qilsa, u holda SyntaxError yoziladi.
Ammo json sintaktik jihatdan toâgâri boâlsa ham, bu uning haqiqiy foydalanuvchi ekanligini anglatmaydi, toâgârimi? Kerakli maâlumotlarni oâtkazib yuborishi mumkin. Masalan, u bizning foydalanuvchilarimiz uchun muhim boâlgan name va age xususiyatlariga ega boâlmasligi mumkin.
Bizning readUser(json) funktsiyasi nafaqat JSONni oâqiydi, balki maâlumotlarni tekshiradi (âtasdiqlaydiâ). Agar talab qilinadigan maydonlar boâlmasa yoki format notoâgâri boâlsa, demak bu xato. Va bu SyntaxError emas, chunki maâlumotlar sintaktik jihatdan toâgâri, ammo boshqa bir xato. Biz buni ValidationError deb ataymiz va buning uchun klass yaratamiz. Bunday xato, shuningdek, hatolik toâgârisidagi maâlumotni oâz ichiga olishi kerak.
Bizning ValidationError klassi oârnatilgan Error klassidan olinishi kerak.
Ushbu klass ichki oârnatilgan, ammo biz nima kengaytirayotganimizni tushunish uchun uning taxminiy kodi bizning koâz oldida boâlishi kerak.
Mana:
// O'rnatilgan Xatolar sinfi uchun "pseudocode" JavaScript-ning o'zi tomonidan belgilanadi
class Error {
constructor(message) {
this.message = message;
this.name = "Error"; // (har xil o'rnatilgan xato klasslari uchun turli xil nomlar)
this.stack = <nested calls>; // nostandart, ammo aksariyat muhitlar uni qo'llab-quvvatlaydi
}
}
Keling, undan ValidationError ni meros qilib olamiz:
class ValidationError extends Error {
constructor(message) {
super(message); // (1)
this.name = "ValidationError"; // (2)
}
}
function test() {
throw new ValidationError("Whoops!");
}
try {
test();
} catch(err) {
alert(err.message); // Whoops!
alert(err.name); // ValidationError
alert(err.stack); // har biri uchun satr raqamlari ko'rsatilgan ichki chaqiruvlar ro'yxati
}
Iltimos, konstruktorga qarang:
(1)satrida biz ota-ona konstruktorini chaqiramiz. JavaScript bolalar konstruktoridasuperni chaqirishni talab qiladi, shuning uchun bu majburiydir. Ota-ona konstruktormessagexususiyatini oârnatadi.- Ota-ona konstruktor shuningdek,
namexususiyatiniErrorga oârnatadi, shuning uchun(2)satrida biz uni kerakli qiymatga qaytaramiz.
Buni readUser(json) da ishlatishga harakat qilaylik:
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
// Usage
function readUser(json) {
let user = JSON.parse(json);
if (!user.age) {
throw new ValidationError("No field: age");
}
if (!user.name) {
throw new ValidationError("No field: name");
}
return user;
}
// Try..catch bilan ishlash namunasi
try {
let user = readUser('{ "age": 25 }');
} catch (err) {
if (err instanceof ValidationError) {
alert("Invalid data: " + err.message); // Invalid data: No field: name
} else if (err instanceof SyntaxError) { // (*)
alert("JSON Syntax Error: " + err.message);
} else {
throw err; // unknown error, rethrow it (**)
}
}
Yuqoridagi koddagi try..catch bloki bizning ValidationError va JSON.parse dan oârnatilgan SyntaxError bilan ishlaydi.
Iltimos, (*) satridagi aniq xato turini tekshirish uchun instanceof dan qanday foydalanayotganimizni koârib chiqing.
Quyidagi kabi err.name ga qarashimiz mumkin:
// ...
// (err instanceof SyntaxError) ning o'rniga
} else if (err.name == "SyntaxError") { // (*)
// ...
instanceof versiyasi ancha yaxshi, chunki kelajakda biz ValidationError ni kengaytiramiz, uning TypeRequiredError kabi pastki turlarini yaratamiz. Va instanceof tekshiruvi yangi meros klasslari uchun ishlashni davom ettiradi. Demak, bu kelajakka ishonchli.
Bundan tashqari, agar catch nomaâlum xatoga duch kelsa, uni (**) satrida qayta tiklashi muhimdir. catch faqat tasdiqlash va sintaksis xatolarini qanday boshqarishni biladi, boshqa turlarni (koddagi xato yoki shunga oâxshash sabablarga koâra) boshqara ololmaydi.
Keyinchalik meros
ValidationError klassi juda umumiy. Koâp narsalar notoâgâri ketishi mumkin. Xususiyat yoâq boâlishi yoki notoâgâri formatda boâlishi mumkin (masalan, age uchun matn qiymati). Keling, aniq xususiyatlar uchun aniqroq PropertyRequiredError klassini yarataylik. Unda yetishmayotgan mulk toâgârisida qoâshimcha maâlumotlar mavjud.
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
class PropertyRequiredError extends ValidationError {
constructor(property) {
super("No property: " + property);
this.name = "PropertyRequiredError";
this.property = property;
}
}
// Foydalanish
function readUser(json) {
let user = JSON.parse(json);
if (!user.age) {
throw new PropertyRequiredError("age");
}
if (!user.name) {
throw new PropertyRequiredError("name");
}
return user;
}
// Try..catch bilan ishlash namunasi
try {
let user = readUser('{ "age": 25 }');
} catch (err) {
if (err instanceof ValidationError) {
alert("Invalid data: " + err.message); // Invalid data: No property: name
alert(err.name); // PropertyRequiredError
alert(err.property); // name
} else if (err instanceof SyntaxError) {
alert("JSON Syntax Error: " + err.message);
} else {
throw err; // noma'lum xato, uni qayta tiklang
}
}
Yangi PropertyRequiredError klassidan foydalanish oson: biz faqat xususiyat nomini kiritishimiz kerak: new PropertyRequiredError(property). Inson tomonidan oâqiladigan message konstruktor tomonidan yaratilgan.
Shuni esda tutingki, PropertyRequiredError konstruktoridagi this.name yana qoâlda tayinlangan. Bu biroz zerikarli boâlishi mumkin â har bir maxsus xatoni yaratishda this.name = <class name> ni belgilash. Ammo chiqishning bir usuli bor. Ushbu yukni yelkamizdan olib tashlaydigan oâzimizning âasosiy xatolarâ klassimizni konstruktorda this.name uchun this.constructor.name dan foydalanishimiz mumkin. Va keyin undan meros olamiz.
Keling, uni MyError deb nomlaymiz.
Soddalashtirilgan MyError va boshqa maxsus xato klasslari bilan kod:
class MyError extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name;
}
}
class ValidationError extends MyError { }
class PropertyRequiredError extends ValidationError {
constructor(property) {
super("No property: " + property);
this.property = property;
}
}
// ism to'g'ri
alert( new PropertyRequiredError("field").name ); // PropertyRequiredError
Endi konstruktordagi "this.name = ..." satridan xalos boâlganimiz sababli, maxsus xatolar ancha qisqaroq, ayniqsa ValidationError.
Istisnolarni oârash
Yuqoridagi koddagi readUser funktsiyasining maqsadi âfoydalanuvchi maâlumotlarini oâqishâ, shunday emasmi? Jarayonda turli xil xatolar boâlishi mumkin. Hozir bizda SyntaxError va ValidationError mavjud, ammo kelajakda readUser funktsiyasi oâsishi mumkin: yangi kod boshqa turdagi xatolarni keltirib chiqarishi mumkin.
readUser ni chaqiradigan kod ushbu xatolarni hal qilishi kerak. Hozirda u turli xil xato turlarini tekshirish va nomaâlumlarini qayta tiklash uchun catch blokida bir nechta if dan foydalanadi. Agar readUser funktsiyasi bir nechta xatolarni keltirib chiqaradigan boâlsa, unda biz oâzimizga savol berishimiz kerak: biz readUser ni chaqiradigan har bir kodda barcha xato turlarini birma-bir tekshirishni xohlaymizmi?
Koâpincha javob âYoâqâ: tashqi kod âhamma narsadan bir darajaâ boâlishni xohlaydi. U qandaydir âmaâlumotlarni oâqish xatosiâ ga ega boâlishni xohlaydi. Nima uchun aynan shunday sodir boâldi â koâpincha ahamiyatsiz (xato xabari uni tavsiflaydi). Yoki xato tafsilotlarini olishning bir usuli boâlsa ham yaxshiroq, lekin faqat agar kerak boâlsa.
Shunday qilib, bunday xatolarni aks ettirish uchun yangi ReadError klassini yarataylik. Agar readUser da xatolik yuz bersa, biz uni oâsha yerda ushlaymiz va ReadError ni yaratamiz. Shuningdek, biz uning asl sababidagi asl xato haqida maâlumotni saqlaymiz. Keyin tashqi kod faqat ReadError ni tekshirishi kerak.
ReadError ni belgilaydigan va uni readUser va try..catch da ishlatilishini koârsatadigan kod:
class ReadError extends Error {
constructor(message, cause) {
super(message);
this.cause = cause;
this.name = 'ReadError';
}
}
class ValidationError extends Error { /*...*/ }
class PropertyRequiredError extends ValidationError { /* ... */ }
function validateUser(user) {
if (!user.age) {
throw new PropertyRequiredError("age");
}
if (!user.name) {
throw new PropertyRequiredError("name");
}
}
function readUser(json) {
let user;
try {
user = JSON.parse(json);
} catch (err) {
if (err instanceof SyntaxError) {
throw new ReadError("Syntax Error", err);
} else {
throw err;
}
}
try {
validateUser(user);
} catch (err) {
if (err instanceof ValidationError) {
throw new ReadError("Validation Error", err);
} else {
throw err;
}
}
}
try {
readUser('{bad json}');
} catch (e) {
if (e instanceof ReadError) {
alert(e);
// Original error: SyntaxError: Unexpected token b in JSON at position 1
alert("Original error: " + e.cause);
} else {
throw e;
}
}
Yuqoridagi kodda readUser aniq taâriflanganidek ishlaydi â sintaksis va tasdiqlash xatolarini ushlaydi va uning oârniga ReadError xatolarini chiqaradi (nomaâlum xatolar odatdagidek qayta koârib chiqiladi).
Shunday qilib tashqi kod instanceof ReadError ni tekshiradi. Mumkin boâlgan barcha xato turlarini roâyxatlashning hojati yoâq.
Ushbu yondashuv âistisnolarni oârashâ deb nomlanadi, chunki biz âpast darajadagi istisnolarniâ qabul qilamiz va ularni ReadError ga oâramoqdamiz, bu esa chaqiruv kodi uchun qulayroq. U obyektga yoânaltirilgan dasturlashda keng qoâllaniladi.
Xulosa
- Odatda
Errorva boshqa oârnatilgan xato klasslaridan meros qilib olishimiz mumkin, faqatnamexususiyati haqida gâamxoârlik qilishimiz kerak vasuperga chaqiruv qilishni unutmang. - Koâpincha, baâzi xatolarni tekshirish uchun
instanceofdan foydalanishimiz kerak. Shuningdek, u meros bilan ishlaydi. Ammo baâzida bizda uchinchi tomon kutubxonasidan xato obyekti kelib chiqadi va klassni olishning oson yoâli yoâq. Bunday tekshirishlar uchunnamexususiyati ishlatilishi mumkin. - Istisnolarni oârash â bu funktsiya past darajadagi istisnolarni koârib chiqishda va xatolar haqida xabar berish uchun yuqori darajadagi obyektni yaratishda keng tarqalgan usul. Baâzan past darajadagi istisnolar yuqoridagi misollarda
err.causekabi obyektning xususiyatlariga aylanadi, ammo bu qatâiy talab qilinmaydi.
Izohlar
<code>yorlig'ini ishlating, bir nechta satrlar uchun - ularni<pre>yorlig'i bilan o'rab qo'ying, 10 satrdan ortiq bo'lsa - sandbox (plnkr, jsbin, codepenâ¦)