Канчалык жакшы программа түзсөк да, кээде биздин сценарийлерибизде мүчүлүштүктөр болот. Алар биздин каталарыбыздан, колдонуучунун күтүүсүз киргизүүсүнөн, сервердин туура эмес жообунан жана башка миңдеген себептерден улам пайда болушу мүмкүн.
Адатта, скрипт ката болгондо "түшүп кетет" (дароо токтойт), ката консолго чыгарылат.
try..catch
Бирок каталарды “кармоого” жана жыгылгандын ордуна маңыздуураак иш кылууга мүмкүндүк берген синтаксистик конструкция бар .
Синтаксиси "аракет... карма"
Дизайн try..catch
эки негизги блоктон турат: try
, анан catch
:
@A@try {
// код...
} catch (err) {
// обработка ошибки
}@A@
Ал мындай иштейт:
- Адегенде блоктун ичиндеги код аткарылат
try {...}
. - Эгерде анда каталар жок болсо, анда блок
catch(err)
этибарга алынбайт: аткаруу аягына чейин жетипtry
, андан ары толугу менен өткөрүп жиберетcatch
. - Эгерде анда ката пайда болсо, анда аткаруу
try
үзгүлтүккө учурап, башкаруу агымы башына өтөтcatch(err)
. Өзгөрмөerr
(ар кандай аталышты колдонсо болот) эмне болгондугу жөнүндө толук маалымат менен ката объектисин камтыйт.
Ошентип, блокто ката пайда болгондо, try {…}
скрипт бузулбайт жана биз catch
.
Келгиле, мисалдарды карап көрөлү.
-
Катасыз мисал: басып чыгаруу
alert
(1)
жана(2)
:@A@try { alert('Начало блока try'); // (1) <-- // ...код без ошибок alert('Конец блока try'); // (2) <-- } catch(err) { alert('Catch игнорируется, так как нет ошибок'); // (3) }@A@
-
Каталары бар мисал: чыгарат
(1)
жана(3)
:@A@try { alert('Начало блока try'); // (1) <-- lalala; // ошибка, переменная не определена! alert('Конец блока try (никогда не выполнится)'); // (2) } catch(err) { alert(`Возникла ошибка!`); // (3) <-- }@A@
try..catch
кодду аткаруу учурунда пайда болгон каталар үчүн гана иштейтИштөө үчүн try..catch
код аткарылуучу болушу керек. Башка сөз менен айтканда, ал жарактуу JavaScript коду болушу керек.
Эгер код синтаксистик жактан туура эмес болсо, ал иштебейт, мисалы, анда тармал кашаалардын саны дал келбеген:
@A@try {
{{{{{{{{{{{{
} catch(e) {
alert("Движок не может понять этот код, он некорректен");
}@A@
JavaScript кыймылдаткычы адегенде кодду окуп, анан аны аткарат. Окуу фазасында пайда болгон каталар талдоо каталары деп аталат. Аларды иштетүү мүмкүн эмес (бул коддун ичинен), анткени кыймылдаткыч кодду түшүнбөйт.
Ошентип, try..catch
ал жарактуу коддо пайда болгон каталарды гана чече алат. Мындай каталар "иштөө учурундагы каталар" жана кээде "өзгөчө" деп аталат.
try..catch
синхрондуу иштейт"Келечек үчүн" пландаштырылган коддо пайда болгон өзгөчө жагдай, мисалы setTimeout
, try..catch
кармалбайт:
@A@try {
setTimeout(function() {
noSuchVariable; // скрипт упадёт тут
}, 1000);
} catch (e) {
alert( "не сработает" );
}@A@
Себеби, функция кийинчерээк, кыймылдаткыч конструкциядан чыгып кеткенде аткарылат try..catch
.
Пландаштырылган функциянын ичиндеги өзгөчөлүктү кармоо үчүн, try..catch
ошол функциянын ичинде болушу керек:
@A@setTimeout(function() {
try {
noSuchVariable; // try..catch обрабатывает ошибку!
} catch {
alert( "ошибка поймана!" );
}
}, 1000);@A@
Ката объекти
Качан ката пайда болгондо, JavaScript анын чоо-жайын камтыган объектти жаратат. Бул объект блокко аргумент катары берилет catch
:
@A@try {
// ...
} catch(err) { // <-- объект ошибки, можно использовать другое название вместо err
// ...
}@A@
Бардык орнотулган каталар үчүн бул объект эки негизги касиетке ээ:
name
Ката аты. Мисалы, аныкталбаган өзгөрмө үчүн бул "ReferenceError"
.
message
Ката тууралуу маалымат.
Көпчүлүк шарттарда, башка стандарттуу эмес касиеттери да бар. Эң кеңири колдонулган жана колдоого алынгандардын бири:
stack
Учурдагы чалуулар стек: Катага алып келген уяча чалуулардын ырааттуулугу жөнүндө маалыматты камтыган сап. Мүчүлүштүктөрдү оңдоо максатында колдонулат.
Мисалы:
@A@try {
lalala; // ошибка, переменная не определена!
} catch(err) {
alert(err.name); // ReferenceError
alert(err.message); // lalala is not defined
alert(err.stack); // ReferenceError: lalala is not defined at (...стек вызовов)
// Можем также просто вывести ошибку целиком
// Ошибка приводится к строке вида "name: message"
alert(err); // ReferenceError: lalala is not defined
}@A@
өзгөрмөсүз "кармап" блогу
Эгер бизге катанын чоо-жайы керек болбосо, catch
аны өткөрүп жиберсек болот:
@A@try {
// ...
} catch { // <-- без (err)
// ...
}@A@
"try... catch" колдонуу
Келгиле, чыныгы колдонуу учурларын карап көрөлү try..catch
.
Биз билгендей, JavaScript JSON окуу үчүн JSON.parse(str) ыкмасын колдойт .
Ал адатта тармак аркылуу, серверден же башка булактан алынган маалыматтарды чечмелөө үчүн колдонулат.
Биз аларды алабыз жана аларды JSON.parse
мындай деп атайбыз:
@A@let json = '{"name":"John", "age": 30}'; // данные с сервера
let user = JSON.parse(json); // преобразовали текстовое представление в JS-объект
// теперь user - объект со свойствами из строки
alert( user.name ); // John
alert( user.age ); // 30@A@
JSON жөнүндө кеңири маалыматты JSON форматында, toJSON ыкмасы бөлүмүндө таба аласыз .
Эгер json
туура эмес болсо, JSON.parse
анда ката пайда болот, башкача айтканда, скрипт бузулат.
Бул жүрүм-турум бизге туура келеби? Албетте жок!
Көрсө, күтүлбөгөн жерден маалыматтарда бир нерсе туура эмес болсо, анда конок эч качан (албетте, консолду ачпаса) бул жөнүндө билбейт экен. Ал эми эч кандай ката билдирүүсү жок бир нерсе "жөн эле бузулуп калган" адамдарга чындап эле жакпайт.
try..catch
Келгиле, катаны чечүү үчүн колдонолу :
@A@let json = "{ некорректный JSON }";
try {
let user = JSON.parse(json); // <-- тут возникает ошибка...
alert( user.name ); // не сработает
} catch (e) {
// ...выполнение прыгает сюда
alert( "Извините, в данных ошибка, мы попробуем получить их ещё раз." );
alert( e.name );
alert( e.message );
}@A@
Бул жерде биз блокту catch
билдирүүнү көрсөтүү үчүн гана колдонобуз, бирок биз дагы көп нерселерди жасай алабыз: жаңы тармак өтүнүчүн жөнөтүү, келүүчүгө альтернативалуу жолду сунуштоо, ката маалыматын серверге каттоо үчүн жөнөтүү, ... жөн эле кыйроого.
Өз каталарын түзүү
Ал json
синтаксистик жактан туура болсо, бирок талап кылынган касиетти камтыбасачы name
?
Мисалы, бул сыяктуу:
@A@let json = '{ "age": 30 }'; // данные неполны
try {
let user = JSON.parse(json); // <-- выполнится без ошибок
alert( user.name ); // нет свойства name!
} catch (e) {
alert( "не выполнится" );
}@A@
Бул JSON.parse
катасыз иштейт, бирок чындыгында мүлктүн жоктугу name
биз үчүн ката.
Ката менен иштөөнү бирдиктүү кылуу үчүн биз throw
.
"ыргытуу" оператору
Оператор throw
ката жаратат.
Синтаксис:
throw <объект ошибки>
Техникалык жактан алганда, бардык нерсе ката объектиси катары берилиши мүмкүн. Ал жада калса примитив, сан же сап болушу мүмкүн, бирок объект болгону жакшыраак, эң жакшысы касиеттери name
жана message
(курулган каталар менен шайкештик үчүн).
JavaScript'те көптөгөн орнотулган стандарттык ката конструкторлор бар: Error
, SyntaxError
, ReferenceError
, TypeError
жана башкалар. Аларды ката объектилерин түзүү үчүн да колдоно аласыз.
Алардын синтаксиси:
@A@let error = new Error(message);
// или
let error = new SyntaxError(message);
let error = new ReferenceError(message);
// ...@A@
Камтылган каталар үчүн (кандайдыр бир объект үчүн эмес, каталар үчүн) касиет name
так конструктордун аты болуп саналат. Ал эми мүлкү message
талаш-тартыштан алынат.
Мисалы:
@A@let error = new Error(" Ого, ошибка! o_O");
alert(error.name); // Error
alert(error.message); // Ого, ошибка! o_O@A@
Келгиле, ал кандай ката кетирерин карап көрөлү JSON.parse
:
@A@try {
JSON.parse("{ bad json o_O }");
} catch(e) {
alert(e.name); // SyntaxError
alert(e.message); // Unexpected token b in JSON at position 2
}@A@
Биз көрүп тургандай, бул SyntaxError
.
Биздин учурда, менчиктин жоктугу name
ката, анткени колдонуучулардын аттары болушу керек.
Аны жараталы:
@A@let json = '{ "age": 30 }'; // данные неполны
try {
let user = JSON.parse(json); // <-- выполнится без ошибок
if (!user.name) {
throw new SyntaxError("Данные неполны: нет имени"); // (*)
}
alert( user.name );
} catch(e) {
alert( "JSON Error: " + e.message ); // JSON Error: Данные неполны: нет имени
}@A@
Онлайнда (*)
билдирүү билдирүү менен throw
катаны жаратат . JavaScript өзү жараткандай эле. Блоктун аткарылышы дароо токтойт жана башкаруу агымы .SyntaxError
message
try
catch
Эми блок catch
бардык каталарды чече турган жалгыз жер болуп калат: JSON.parse
башка учурларда да.
Өзгөчө учуруу
Жогорудагы мисалда биз try..catch
туура эмес маалыматтарды иштетчүбүз. Блокто дагы бир күтүүсүзtry {...}
ката болсочу ? Мисалы, туура эмес маалыматтар менен байланышкан ката эмес, программа (аныкталбаган өзгөрмө) же башка нерсе.
Мисал:
@A@let json = '{ "age": 30 }'; // данные неполны
try {
user = JSON.parse(json); // <-- забыл добавить "let" перед user
// ...
} catch(err) {
alert("JSON Error: " + err); // JSON Error: ReferenceError: user is not defined
// (не JSON ошибка на самом деле)
}@A@
Албетте, баары мүмкүн! Программисттер ката кетиришет. Миллиондогон адамдар ондогон жылдар бою колдонгон ачык булактуу утилиталарда да, коркунучтуу хакерлерге алып келген ката күтүлбөгөн жерден табылышы мүмкүн.
Биздин учурда, try..catch
ал туура эмес маалыматтар менен байланышкан каталарды аныктоо үчүн иштелип чыккан. Бирок өзүнүн табияты боюнча бардыкcatch
каталарын . Бул жерде ал күтүлбөгөн ката алат, бирок дагы эле ошол эле билдирүүнү көрсөтөт . Бул туура эмес жана кодду оңдоону кыйындатат.try
"JSON Error"
Бактыга жараша, биз, мисалы, анын касиетине карап, кайсы ката кетиргенибизди биле алабыз name
:
@A@try {
user = { /*...*/ };
} catch(e) {
alert(e.name); // "ReferenceError" из-за неопределённой переменной
}@A@
Жөнөкөй эреже бар:
Блок catch
өзүнө белгилүү болгон каталарды гана иштеп чыгып, калгандарынын баарын "алга" алышы керек.
ыргытуу өзгөчө техникасы мындай көрүнөт:
- Блок
catch
бардык каталарды кабыл алат. - Блокто
catch(err) {...}
биз ката объектисин талдайбызerr
. - Эгерде биз аны кантип иштетүүнү билбесек, анда биз аны жасайбыз
throw err
.
Төмөнкү коддо биз өзгөчөлүктү колдонушат, catch
ал бир гана иштетет SyntaxError
:
@A@let json = '{ "age": 30 }'; // данные неполны
try {
let user = JSON.parse(json);
if (!user.name) {
throw new SyntaxError("Данные неполны: нет имени");
}
blabla(); // неожиданная ошибка
alert( user.name );
} catch(e) {
if (e.name == "SyntaxError") {
alert( "JSON Error: " + e.message );
} else {
throw e; // проброс (*)
}
}@A@
(*)
Блоктун сызыгындагы ката catch
"түшүп кетет" жана башка тышкы конструкция тарабынан кармалышы мүмкүн try..catch
(эгерде бар болсо) же сценарийди "өлтүрөт".
Ошентип, блок catch
чындыгында ал кантип чечүү керектигин билген каталарды гана чечет жана калганын өткөрүп жиберет.
Төмөндөгү мисал мындай каталарды дагы башка деңгээл менен кантип кармоону көрсөтөт try..catch
:
@A@function readData() {
let json = '{ "age": 30 }';
try {
// ...
blabla(); // ошибка!
} catch (e) {
// ...
if (e.name != 'SyntaxError') {
throw e; // проброс исключения (не знаю как это обработать)
}
}
}
try {
readData();
} catch (e) {
alert( "Внешний catch поймал: " + e ); // поймал!
}@A@
Бул жерде readData
ал кантип иштетүүнү гана билет SyntaxError
, ал эми сырткы блок try..catch
баарын кантип иштетүүнү билет.
аракет ... карма ... акыры
Күтө туруңуз, бул баары эмес.
Түзүм try..catch
дагы бир бөлүмдү камтышы мүмкүн: finally
.
Эгерде бөлүм бар болсо, анда ал баары бир аткарылат:
- кийин
try
, эч кандай ката жок болсо, - кийин
catch
каталар бар болсо.
Кеңейтилген синтаксис төмөнкүдөй көрүнөт:
@A@try {
... пробуем выполнить код...
} catch(e) {
... обрабатываем ошибки ...
} finally {
... выполняем всегда ...
}@A@
Бул кодду иштетип көрүңүз:
@A@try {
alert( 'try' );
if (confirm('Сгенерировать ошибку?')) BAD_CODE();
} catch (e) {
alert( 'catch' );
} finally {
alert( 'finally' );
}@A@
Коддун эки аткаруу жолу бар:
- Эгер сиз "Ката түзөсүзбү?" деген суроого жооп берсеңиз. ырастап, анда
try -> catch -> finally
. - Эгер сиз терс жооп берсеңиз,
try -> finally
анда
Бөлүм finally
көбүнчө ката барбы же жокпу, бир нерсени жасап баштаганда жана аны аягына чыгаргыбыз келгенде колдонулат.
Мисалы, биз Fibonacci саны функциясы талап кылган убакытты өлчөөнү каалайбыз fib(n)
. Албетте, биз өлчөөнү функция аткарыла электе баштап, андан кийин бүтүрө алабыз. Бирок функцияны чакырып жатканда ката чыксачы? Атап айтканда, fib(n)
төмөндөгү коддогу ишке ашыруу терс жана бүтүн эмес сандар үчүн катаны кайтарат.
Бөлүм finally
эч нерсеге карабастан өлчөөлөрдү аяктоо үчүн эң сонун.
Бул жерде finally
убакыт эки жагдайда туура өлчөнөт деп кепилдик берет - ийгиликтүү учурда да fib
, ката болгон учурда да:
@A@let num = +prompt("Введите положительное целое число?", 35)
let diff, result;
function fib(n) {
if (n < 0 || Math.trunc(n) != n) {
throw new Error("Должно быть целое неотрицательное число");
}
return n <= 1 ? n : fib(n - 1) + fib(n - 2);
}@A@
@A@let start = Date.now();
try {
result = fib(num);
} catch (e) {
result = 0;
} finally {
diff = Date.now() - start;
}
alert(result || "возникла ошибка");
alert( `Выполнение заняло ${diff}ms` );@A@
Сиз муну бул кодду иштетүү менен текшере аласыз жана терүү 35
- prompt
код кадимкидей бүтөт, finally
кийин аткарылат try
. Анан кириңиз -1
- ката дароо пайда болот, аткарууну талап кылат 0ms
. Эки өлчөө тең туура.
Башкача айтканда, функция кандайча аяктаганы маанилүү эмес: аркылуу return
же throw
. Бөлүм finally
эки учурда тең иштейт.
try..catch..finally
жергиликтүүЖогорудагы коддогу өзгөрмөлөр result
жана мурун жарыяланганын эске алыңыз .diff
try..catch
Эгерде өзгөрмө блокто, мисалы, ичинде жарыяланса try
, анда ал андан кийин жеткиликтүү болбойт.
finally
Жанаreturn
Блок , анын ичинде ар кандай чыгууда finally
иштетилет .try..catch
return
Төмөндөгү мисалда, from try
пайда болот return
, бирок finally
башкаруу тышкы кодго кайтарылганга чейин башкарууну алат.
@A@function func() {
try {
return 1;
} catch (e) {
/* ... */
} finally {
alert( 'finally' );
}
}
alert( func() ); // сначала срабатывает alert из finally, а затем этот код@A@
try..finally
try..finally
Бөлүмсүз дизайн catch
да пайдалуу. Биз аны бул жерде каталарды чечүүнү каалабаганда колдонобуз (алар түшүп калсын), бирок башталган процесстер аяктаганына ишенгибиз келет.
@A@function func() {
// начать делать что-то, что требует завершения (например, измерения)
try {
// ...
} finally {
// завершить это, даже если все упадёт
}
}@A@
Жогорудагы коддо ката дайыма сыртка ыргытылат, анткени catch
. Бирок finally
ал башкаруу агымы функциядан чыкканга чейин иштейт.
глобалдык кармоо
Бул бөлүмдөгү маалымат JavaScript тилинин бир бөлүгү эмес.
Сыртта өлүмгө алып келе турган ката (программалык камсыздоо же башка коркунучтуу нерсе) болуп try..catch
, скрипт бузулуп калганын элестетип көрөлү.
Мындай жагдайларга жооп кайтаруунун жолу барбы? Биз катаны каттап, колдонуучуга бир нерсе көрсөткүбүз келиши мүмкүн (алар адатта ката кабарын көрүшпөйт) ж.б.
Спецификацияда мындай жол жок, бирок чөйрөлөр көбүнчө аны камсыздайт, анткени ал абдан пайдалуу. Мисалы, Node.js бар process.on("uncaughtException")
. Ал эми браузерде биз өзгөчө касиетке функцияны дайындай алабыз window.onerror , ал иштетилбеген ката болгон учурда чакырылат.
Синтаксис:
@A@window.onerror = function(message, url, line, col, error) {
// ...
};@
message
Ката билдирүү.
url
Ката болгон скрипттин URL дареги.
line
,col
Ката болгон сап жана мамычанын номерлери.
error
Ката объекти.
Мисал:
@A@<script>
window.onerror = function(message, url, line, col, error) {
alert(`${message}\n В ${line}:${col} на ${url}`);
};
function readData() {
badFunc(); // Ой, что-то пошло не так!
}
readData();
</script>@A@
Глобалдык иштетүүчүнүн ролу window.onerror
адатта скрипттин аткарылышын калыбына келтирүү эмес - бул программалоо катасы болгон учурда мүмкүн эмес, бирок иштеп чыгуучуларга ката билдирүүсүн жөнөтүү.
Ошондой эле https://errorception.com же http://www.muscula.com сыяктуу учурлар үчүн ката журналын камсыз кылган веб кызматтар бар .
Алар мындай иштешет:
- Биз кызматка катталып, алардан барактарга киргизүү үчүн кичинекей JS скриптин (же скрипт URL) алабыз.
- Бул JS скрипти өзүнүн функциясын коёт
window.onerror
. - Ката пайда болгондо, ал кызматка ал жөнүндө маалымат менен тармактык суроо-талапты аткарат жана жөнөтөт.
- Биз кызматтын веб-интерфейсине кирип, каталарды көрө алабыз.
Бардыгы
Дизайн try..catch
кодду аткаруу учурунда каталарды чечүүгө мүмкүндүк берет. Бул кодду иштетүүгө жана анда пайда болушу мүмкүн болгон каталарды кармоого мүмкүндүк берет.
Синтаксис:
@A@try {
// исполняем код
} catch(err) {
// если случилась ошибка, прыгаем сюда
// err - это объект ошибки
} finally {
// выполняется всегда после try/catch
}@A@
Бөлүмдөр болушу мүмкүн catch
же болбошу мүмкүн finally
, башкача айтканда, кыскараак конструкциялар да try..catch
туура try..finally
.
Ката объекттери төмөнкү касиеттерди камтыйт:
message
- адам окуй турган билдирүү.name
– ката аты бар сап (ката конструкторунун аты).stack
(стандартты эмес, бирок жакшы колдоого алынган) - ката учурунда стек.
Ката объекти керек болбосо, биз аны catch {
ордуна колдонуу менен өткөрүп жиберсек болот catch(err) {
.
Биз өз каталарыбызды колдонуп да түзө алабыз throw
. Аргумент throw
каалаган нерсе болушу мүмкүн, бирок, адатта, орнотулгандан мураска калган ката объектиси болуп саналат Error
. Каталарды кеңейтүү жөнүндө көбүрөөк билүү үчүн кийинки бөлүмдү караңыз.
Өзгөчө учурду ыргытуу каталарды чечүүнүн абдан маанилүү ыкмасы болуп саналат: блок catch
адатта катанын белгилүү бир түрүн кантип чечүүнү күтөт жана билет, ошондуктан ал билбеген каталарды кайра ыргытышы керек.
Бизде жок болсо да try..catch
, көпчүлүк чөйрөлөр "түшүп кеткен" каталарды кармоо үчүн "глобалдык" ката иштеткичти орнотууга мүмкүндүк берет. Браузерде бул window.onerror
.
Tasks
Эки код үзүндүсүн салыштырыңыз.
-
Биринчиси
finally
төмөнкүдөн кийин кодду аткаруу үчүн колдонулатtry..catch
:@A@try { начать работу работать } catch (e) { обработать ошибку } finally { очистить рабочее пространство }@A@
Экинчи үзүндү төмөнкүдөн кийин тазалоону коётtry..catch
: -
@A@try { начать работу работать } catch (e) { обработать ошибку }@A@ очистить рабочее пространство
Бизге жумуштан кийин каталар барбы же жокпу, сөзсүз түрдө тазалоо керек.
Бул жерде колдонуу артыкчылыгы барбы finally
же эки код үзүндүлөрү бирдейби? Андай артыкчылыгы бар болсо, анда ал качан көрүнөрүн мисал келтириңиз.