Обробка помилок "try..catch"
Неважливо, наскільки ми хороші у програмуванні, іноді наші скрипти містять помилки. Вони можуть виникати через наші помилки, несподіване введення користувача, неправильну відповідь сервера та з тисячі інших причин.
Зазвичай скрипт у разі помилки "падає" (відразу ж зупиняється), з виведенням помилки в консоль.
Але є синтаксична конструкція try..catch, яка дозволяє «ловити» помилки і замість падіння робити щось осмислене.
Синтаксис "try ... catch"
Конструкція try..catch складається з двох основних блоків: try , а потім catch :
- Спочатку виконується код усередині блоку try.
- Якщо в ньому немає помилок, то блок catch(err) ігнорується: виконання доходить до кінця try і далі, повністю пропускаючи catch .
- Якщо ж у ньому з'являється помилка, то виконання try переривається, і потік управління перетворюється на початок catch(err) . Змінна err (можна використовувати будь-яке ім'я) містить об'єкт помилки з детальною інформацією про подію.
Таким чином, при помилці в блоці try скрипт не "падає", і ми отримуємо можливість обробити помилку всередині catch.
Давайте розглянемо приклади.
-
Приклад без помилок: виведе alert (1) та (2) :
Щоб try..catch працював, код має бути здійсненним. Іншими словами, це має бути коректний JavaScript-код.
Він не спрацює, якщо код синтаксично невірний, наприклад, містить незбігну кількість фігурних дужок:
JavaScript-движок спочатку читає код, а потім виконує його. Помилки, які виникають під час фази читання, називають помилками парсингу. Їх не можна обробити (зсередини цього коду), тому що двигун не розуміє код.
Таким чином, try..catch може обробляти лише помилки, що виникають у коректному коді. Такі помилки називають помилками під час виконання, а іноді винятками.
Виняток, який відбудеться в коді, запланованому «на майбутнє», наприклад setTimeout , try..catch не зловить:
try < setTimeout(function() < noSuchVariable; // скрипт впаде тут >, 1000); > catch (e)
Це тому, що функція виконується пізніше, коли двигун уже залишив конструкцію try..catch.
Щоб зловити виняток усередині запланованої функції, try..catch повинен знаходитися всередині цієї функції:
setTimeout(function() < try < noSuchVariable; // try..catch обробляє помилку! >catch < alert( "помилка спіймана!"); >>, 1000);
Об'єкт помилки
Коли виникає помилка, JavaScript генерує об'єкт, що містить деталі. Потім цей об'єкт передається як аргумент до блоку catch :
Для всіх вбудованих помилок цей об'єкт має дві основні властивості:
name Ім'я помилки. Наприклад, для невизначеної змінної це "ReferenceError". message Текстове повідомлення про деталі помилки.
У більшості оточень доступні інші, нестандартні властивості. Одне з широко використовуваних і підтримуваних – це:
stack Поточний стек виклику: рядок, який містить інформацію про послідовність вкладених викликів, які призвели до помилки. Використовується з метою налагодження.
Блок "catch" без змінної
Ця можливість була додана в мову нещодавно. У старих браузерах може знадобитися поліфіл.Якщо нам не потрібні деталі помилки, у catch можна її пропустити:
Використання «try…catch»
Давайте розглянемо реальні випадки використання try..catch.
Як ми знаємо, JavaScript підтримує метод JSON.parse(str) для читання JSON.
Зазвичай він використовується для декодування даних, отриманих через мережу, від сервера або з іншого джерела.
Ми отримуємо їх і викликаємо JSON.parse ось так:
let json = ''; // дані із сервера let user = JSON.parse(json); // перетворили текстове уявлення в JS-об'єкт // тепер user - об'єкт з властивостями рядка alert( user.name ); // John alert (user.age); // 30
Ви можете знайти більш детальну інформацію про JSON у розділі Формат JSON, метод toJSON.
Якщо json некоректний, JSON.parse генерує помилку, тобто скрипт "падає".
Чи влаштує нас така поведінка? Звісно ні!
Виходить, якщо раптом щось не так з даними, то відвідувач ніколи (якщо, звичайно, не відкриє консоль) про це не дізнається. А люди дуже не люблять, коли щось просто падає без жодного повідомлення про помилку.
Давайте використовуємо try..catch для обробки помилки:
let json = "<некоректний JSON>"; try < let user = JSON.parse(json); // catch (e) < //. виконання стрибає сюди alert( "Вибачте, в даних помилка, ми спробуємо отримати їх ще раз."); alert(e.name); alert(e.message); >
Тут ми використовуємо блок catch тільки для виведення повідомлення, але ми також можемо зробити набагато більше: відправити новий мережевий запит, запропонувати відвідувачу альтернативний спосіб, надіслати інформацію про помилку на сервер для логування, … Все краще, ніж просто падіння.
Генерація власних помилок
Що якщо json синтаксично коректний, але не містить необхідної властивості name?
let json = '<"age": 30 >'; // дані неповні try <let user=JSON.parse(json); // catch (e)
Тут JSON.parse виконається без помилок, але насправді відсутність властивості name для нас є помилкою.
Для того, щоб уніфікувати обробку помилок, ми скористаємося оператором throw.
Оператор "throw"
Оператор throw створює помилку.
Технічно як об'єкт помилки можна передати будь-що. Це може бути навіть примітив, число або рядок, але все ж таки краще, щоб це був об'єкт, бажано з властивостями name і message (для сумісності з вбудованими помилками).
У JavaScript є безліч вбудованих конструкторів для стандартних помилок: Error, SyntaxError, ReferenceError, TypeError та інші. Можна також використовувати їх для створення об'єктів помилки.
let error = новий Error (message); // або let error = New SyntaxError (message); let error = новий ReferenceError(message); //.
Для вбудованих помилок (не будь-яких об'єктів, лише помилок), властивість name – це точно ім'я конструктора. А якість message береться з аргументу.
let error = new Error("Ого, помилка! o_O"); alert(error.name); //Error alert(error.message); // Ого, помилка! o_O
Давайте подивимося, яку помилку генерує JSON.parse :
try < JSON.parse("< некоректний json o_O >"); > catch(e) <alert(e.name); // SyntaxError alert(e.message); // Відображений property name or '>' в JSON на position 2 (line 1 column 3) >
Як бачимо, це SyntaxError .
У нашому випадку відсутність властивості name – це помилка, адже користувачі мають імена.
let json = '<"age": 30 >'; // дані неповні try <let user=JSON.parse(json); / / alert (user.name); > catch(e) < alert( "JSON Error: " + e.message ); // JSON Error: Дані неповні: немає імені >
У рядку (*) оператор throw генерує помилку SyntaxError з повідомленням message. Такого ж виду, як генерує сам JavaScript. Виконання блоку try негайно зупиняється і потік управління стрибає в catch .
Тепер блок catch стає єдиним місцем обробки всіх помилок: і для JSON.parse і для інших випадків.
Прокидання винятку
У цьому прикладі ми використовували try..catch для обробки некоректних даних. А що якщо в блоці try <. >виникне інша несподівана помилка? Наприклад, програмна (невизначена змінна) або якась ще, а не помилка, пов'язана з некоректними даними.
let json = '<"age": 30 >'; // дані неповні try <user=JSON.parse(json); // catch(err) <alert("JSON Error:" + err); // JSON Error: ReferenceError: user is not defined // (не насправді JSON помилка) >
Звісно, можливо все! Програмісти роблять помилки.Навіть в утилітах з відкритим вихідним кодом, які використовуються мільйонами людей протягом десятиліть – раптом може бути виявлена помилка, яка призводить до жахливих зламів.
У нашому випадку try..catch призначений для виявлення помилок, пов'язаних із некоректними даними. Але за своєю природою catch отримує все свої помилки з try. Тут він отримує несподівану помилку, але все також показує те саме повідомлення "JSON Error" . Це неправильно і ускладнює налагодження коду.
На щастя, ми можемо з'ясувати, яку помилку ми отримали, наприклад, за її властивістю.
Є просте правило:
Блок catch повинен обробляти лише ті помилки, які йому відомі, і «прокидати» всі інші.
Техніка «прокидання виключення» виглядає так:
- Блок catch отримує усі помилки.
- У блоці catch(err) ми аналізуємо об'єкт помилки err.
- Якщо ми не знаємо, як її обробити, тоді робимо throw err .
У коді нижче ми використовуємо прокидання виключення, catch обробляє тільки SyntaxError :
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; // Прокидання (*) >>
Помилка у рядку (*) з блоку catch «випадає назовні» і може бути спіймана іншою зовнішньою конструкцією try..catch (якщо є), або «вб'є» скрипт.
Таким чином, блок catch фактично обробляє ті помилки, з якими він знає, як справлятися, і пропускає інші.
Приклад нижче демонструє, як такі помилки можуть бути спіймані за допомогою ще одного рівня try.
function readData() < let json = '<"age": 30 >'; try <//. blabla(); // Помилка! > catch (e) < // . if (e.name != 'SyntaxError') < throw e; // прокидання винятку (не знаю як це обробити) >> > try < readData(); >catch (e) < alert( " Зовнішній catch спіймав: " + e ); // Впіймав! >
Тут readData знає тільки, як обробити SyntaxError, тоді як зовнішній блок try..catch знає, як обробити все.
try…catch…finally
Зачекайте, що це ще не все.
Конструкція try..catch може мати ще одну секцію: finally .
Якщо секція є, вона виконується у разі:
- після try , якщо не було помилок,
- після catch якщо помилки були.
Розширений синтаксис виглядає так:
try < . пробуємо виконати код. >catch(e) < . обробляємо помилки. >finally
Спробуйте запустити такий код:
try <alert('try'); if (confirm('Сгенерувати помилку?')) BAD_CODE(); >catch (e) <alert( 'catch'); >finally
Код має два шляхи виконання:
- Якщо ви дасте відповідь на запитання «Сгенерувати помилку?» ствердно, то try -> catch -> finally.
- Якщо відповісте негативно, то try -> finally.
Секцію finally часто використовують коли ми почали щось робити і хочемо завершити це незалежно від того, буде помилка чи ні.
Наприклад, хочемо виміряти час, який займає функція чисел Фібоначчі fib(n) . Звичайно, ми можемо почати вимірювання до того, як функція почне виконуватися і закінчити після. Але що робити, якщо під час виклику функції виникла помилка? Зокрема, реалізація fib(n) у коді нижче повертає помилку для негативних чи нецілих чисел.
Секція finally відмінно підходить для завершення вимірювань, незважаючи ні на що.
Тут finally гарантує, що час буде виміряно коректно в обох ситуаціях – і у разі успішного завершення fib та у разі помилки:
let num = +prompt("Введіть позитивне ціле число?", 35) let diff, result; function fib(n) < if (n < 0 || Math.trunc(n) != n) < throw new Error("Має бути ціле невід'ємне число"); >return n let start = Date.now(); try <result = fib(num); > catch (e) <result = 0; >finally < diff = Date.now() - start; >alert(result || "виникла помилка"); alert( `Виконання зайняло $ms` );
Ви можете перевірити це, запустивши цей код і ввівши 35 в prompt – код завершиться нормально, finally виконається після try . А потім введіть -1 – негайно буде помилка, виконання займе 0ms. Обидва виміри виконуються коректно.
Інакше кажучи, неважливо як завершилася функція: через return чи throw . Секція finally спрацьовує в обох випадках.
Зверніть увагу, що змінні result і diff у коді вище оголошені до try..catch.
Якщо змінну оголосити в блоці, наприклад, try , то вона не буде доступна після нього.
Блок finally спрацьовує у будь-кому виході з try..catch, у тому числі і return.
У прикладі нижче try відбувається return , але finally отримує управління до того, як контроль повертається до зовнішнього коду.
function func() < try < return 1; >catch (e) < /* . */ >finally < alert( 'finally' ); >> alert(func()); // Спершу спрацьовує alert з finally, а потім цей код
Конструкція try..finally без секції catch також корисна. Ми застосовуємо її, коли не хочемо тут обробляти помилки (нехай випадуть), але хочемо бути впевнені, що розпочаті процеси завершилися.
function func() < // почати робити щось, що вимагає завершення (наприклад, виміру) try < // . >finally < // завершити це, навіть якщо все впаде >>
У наведеному вище коді помилка завжди випадає назовні, тому що тут немає блоку catch. Але finally відпрацьовує перед тим, як потік управління вийде з функції.
Глобальний catch
Інформація з цієї секції не є частиною JavaScript.
Давайте уявимо, що відбулася фатальна помилка (програмна або щось жахливе) зовні try..catch , і скрипт впав.
Чи існує спосіб відреагувати на такі ситуації?
Такого способу немає в специфікації, але зазвичай оточення надають його, тому що це дуже корисно. Наприклад, в Node.js для цього є process.on("uncaughtException") . буде викликана у разі необробленої помилки.
window.onerror = function(message, url, line, col, error) < // .
message Повідомлення про помилку URL скрипта, в якому відбулася помилка line , col Номери рядка і стовпця, в яких відбулася помилка.
Роль глобального обробника window.onerror зазвичай полягає не у відновленні виконання скрипту - це швидше за все неможливо у випадку програмної помилки, а у відправленні повідомлення про помилку розробникам.
Існують також веб-сервіси, які надають логування помилок для таких випадків, як https://errorception.com або https://www.muscula.com.
- Ми реєструємося в сервісі та отримуємо невеликий JS-скрипт (або URL-адреса скрипта) від них для вставки на сторінки.
- Цей JS-скрипт ставить свою функцію window.onerror.
- Коли виникає помилка, вона виконується та надсилає мережевий запит з інформацією про неї у сервіс.
- Ми можемо увійти до веб-інтерфейсу сервісу і побачити помилки.
Разом
Конструкція try..catch дозволяє обробляти помилки під час виконання коду. Вона дозволяє запустити код і перехопити помилки, які можуть виникнути.
try < // виконуємо код >catch(err) < // якщо трапилася помилка, стрибаємо сюди // err - це об'єкт помилки >finally < // виконується завжди після try/catch >
Секцій catch або finally може не бути, тобто коротші конструкції try..catch і try..finally також коректні.
Об'єкти помилок містять такі властивості:
- message - зрозуміле людині повідомлення.
- name – рядок із ім'ям помилки (ім'я конструктора помилки).
- stack (нестандартний, але добре підтримується) – стек на момент помилки.
Якщо об'єкт помилки не потрібен, ми можемо пропустити його за допомогою catch < замість catch(err) < .
Ми також можемо генерувати власні помилки, використовуючи оператор throw . Аргументом throw може бути будь-що, але зазвичай це об'єкт помилки, успадкований від вбудованого класу Error . Докладніше про розширення помилок див. у наступному розділі.
Прокидання винятку - це дуже важливий прийом обробки помилок: блок catch зазвичай чекає і знає, як обробити певний тип помилок, тому він повинен прокидати далі помилки, про які він не знає.
Навіть якщо у нас немає try..catch, більшість середовищ дозволяють налаштувати «глобальний» обробник помилок, щоб ловити помилки, які «випадають назовні». У браузері це window.onerror.
Обробка помилок, try. catch у JavaScript
Помилки коду можуть виникати з різних причин. Наприклад, ви відправили запит на сервер, а він дав збій і надіслав відповідь, яка призвела до несподіваних наслідків. Крім цього, можуть бути тисячі інших, а також свої власні.
Коли виникає помилка, виконання коду припиняється, і ця помилка виводиться в консоль:
const json = '{name:"Олександр"}'; const person = JSON.parse(json); // Uncaught SyntaxError: Unexpected token n в JSON на position 1 console.log('Це повідомлення ми не побачимо!');
Виконання цього прикладу зупиниться при парсингу рядка JSON. У консоль буде виведена непоймана помилка (uncaught error). Вона так називається, тому що ми її не впіймали (не обробили). Далі код виконуватися не буде і повідомлення, яке ми виводимо за допомогою console.log() не відобразиться.
try. catch
Обробка помилок у JavaScript здійснюється за допомогою try. catch.
try. catch - це спеціальний синтаксис, що складається з 2 блоків коду:
try { // блок коду, в якому є можливість виникнення помилки } catch(error) { // цей блок виконується тільки у разі виникнення помилки в блоці try }
Перший блок йде відразу після ключового слова try. У цей блок ми поміщаємо частину коду, де є ймовірність виникнення помилки.
Другий блок розташовується за ключовим словом catch. У нього поміщаємо код, який буде виконано лише в тому випадку, якщо у першому блоці виникне помилка. У круглих дужках після catch вказуємо параметр error. У цьому параметрі буде поміщена помилка, яка виникла в блоці try .
Код, наведений ми повернули у try. catch, а саме ту його частину, в якому може виникнути помилка:
const text = '{name:"Олександр"}'; try {const person = JSON.parse(text); // Uncaught SyntaxError: Unexpected token n в JSON at position 1 } catch(error) { console.error(error); console.log(error.message); } console.log('Це повідомлення ми побачимо!');
Тут у блоці try станеться помилка, тому що в даному прикладі ми спеціально надали змінній текст некоректний рядок JSON. У catch цю помилку буде присвоєно параметру error , і ми будемо просто виводити цю помилку в консоль за допомогою console.error() . Таким чином, вона буде виведена також червоним кольором, але без слова Uncaught, т.к. ця помилка була спіймана.
Помилка – це об'єкт і має такі свойства:
- message - Опис помилки;
- name – тип помилки, наприклад, RangeError при вказівці значення діапазону, що виходить за межі;
- stack – рядок стека, який використовується з метою налагодження; вона дозволяє дізнатися про те, що відбувалося у скрипті на момент виникнення помилки.
У цьому прикладі ми також написали інструкцію для виведення опису помилки error.message в консоль за допомогою console.log() .
Приклад функції перевірки коректності JSON:
const isValidJSON = (text) => {try { JSON.parse(text); return true; } catch { return false; } }
При виклику функції спочатку буде виконуватися інструкція JSON.parse(text) . Якщо помилки не виникне, то повернеться значення true. Інакше інтерпретатор перейде в секцію catch . У результаті буде повернуто false. До речі, тут catch записаний без вказівки круглих дужок і параметра всередині них. Ця можливість була додана до мови, починаючи з версії ECMAScript 2019.
Блок "finally"
У JavaScript можливі три форми інструкції try:
Блок finally виконується завжди, незалежно від того виникли помилки у try чи ні. Він виконується після try якщо помилок не було, і після catch , якщо помилки були. Секція не має параметрів.
Приклад з використанням finally:
let result = 0; try {result = sum(10, 20); console.log('Це повідомлення ми не побачимо!'); } catch(error) { console.log(error.message); } finally { console.log(result); }
У цьому прикладі відбудеться помилка в секції try, тому що sum ніде не визначена. Після помилки інтерпретатор перейдемо в catch . За допомогою методу console.log() повідомлення про помилку буде виведено в консоль. Потім виконається інструкція, що у блоці finally .
JavaScript також має конструкцію без catch :
try {//. } finally { // завершуємо якісь дії }
Інструкція throw
JavaScript має інструкцію throw , яка дозволяє генерувати помилку.
Синтаксис інструкції throw :
Як правило, як вираз зазвичай використовують вбудований основний клас для помилок Error або більш конкретний, наприклад: RangeError , ReferenceError , SyntaxError , TypeError , URIError або інший.
Створюємо новий об'єкт Error і викидаємо його як виняток:
throw new Error('Якийсь опис помилки');
Приклад генерування синтаксичної помилки:
throw new SyntaxError('Опис помилки');
Як вираз можна використовувати як об'єкт помилки, а й рядки, числа, логічні значення та інші величини. Але робити це не рекомендується:
throw 'Значення не є числом';
При виявленні оператора throw виконання коду припиняється і помилка викидається в консоль.
Наприклад, створимо функцію, яка просто викидатиме нову помилку:
// Створюємо стрілочну функцію і присвоюємо її змінною myFn const myFn = () => { throw new Error ('Опис помилки'); } // Викликаємо функцію myFn(); console.log('Це повідомлення ми не побачимо в консолі!');
Для обробки помилки обернемо виклик функції в try. catch:
const myFn = () => { throw new Error('Опис помилки'); } try {myFn(); } catch(error) { console.error(error); } console.log('Це повідомлення ми побачимо в консолі!');
У цьому прикладі ви побачите в консолі помилку і далі повідомлення, які ми виводимо за допомогою console.log(). Тобто виконання коду продовжиться.
Крім вбудованих класів помилок можна створити свої власнінаприклад, шляхом розширення Error :
class FormError extends Error { constructor(message) { super(message); this.name = 'FormError'; } }
Використання свого класу FormError для відображення помилок форми:
Глобальний лов помилок
Виникнення помилок, які ми ніяк не обробляємо за допомогою try, можна дуже просто перехопити за допомогою window.onerror:
window.onerror = function(message, source, lineno, colno, error) { // . }
Цей анонімний функціональний вираз викликатиметься щоразу при виникненні непойманої помилки. Їй передаються аргументи, які ми отримуватимемо за допомогою наступних параметрів:
- message - рядок, що містить повідомлення про помилку;
- source - URL-адреса скрипта або документа, в якому сталася помилка;
- lineno та colno - відповідно номер рядка та стовпця, в якому сталася помилка;
- error - об'єкт помилки або null якщо відповідний об'єкт помилки недоступний;
Передача помилок на сервер
Що робити із цими помилками? Їх, наприклад, можна передавати на сервер щоб пізніше можна було проаналізувати ці помилки і вжити заходів щодо їх усунення.
Приклад коду для надсилання помилок, що виникають у браузері на сервер через AJAX з використанням fetch:
window.onerror = (message, source, lineno, colno) => { const err = { message, source, lineno, colno }; fetch('/assets/php/error-log.php', { method: 'post', body: JSON.stringify(err)}); }
На сервері, якщо, наприклад, сайт на PHP, можна написати такий простенький скрипт:
Його слід зберегти у файл /assets/php/error-log.php , а також у цьому каталозі створити папку logs для збереження в ній логів.
В результаті коли на клієнті, тобто в браузері буде виникати помилки JavaScript, вони будуть зберігатися на сервер у файл наступним чином:
try. catch
Ця особливість є добре створеною і роботою, пов'язаною з багатьма пристроями і розробниками версій. It's been available across browsers since July 2015 .
Конструкція try. catch намагається виконати інструкції в блоці try і, у разі помилки, виконує блок catch .
Синтаксис
try < try_statements > [catch (exception_var_1 if condition_1) < // не стандартно catch_statements_1 >] . [catch (exception_var_2) < catch_statements_2 >] [finally < finally_statements >]
Інструкції до виконання.
Інструкції, які будуть виконані, якщо буде помилка в блоці try .
Ідентифікатор для зберігання об'єкта помилки, який згодом використовується у блоці catch
Інструкції, які виконуються після завершення блоку try . Виконання відбувається незалежно від того, сталася помилка чи ні.
Опис
Конструкція try містить блок try , в якому знаходиться одна або кілька інструкцій (Блок (<> ) обов'язково повинен бути присутнім, навіть якщо виконується лише одна інструкція), і хоча б один блок catch або finally . Таким чином, є три основні форми конструкції try :
Блок catch містить інструкції, які будуть виконані, якщо в блоці try сталася помилка.Якщо будь-яка інструкція в блоці try викидає виняток, то керування відразу переходить у блок catch . Якщо блок try не було викинуто виняток, то блок catch не виконується.
Блок finally виконається після виконання блоків try і catch, але перед інструкціями, що йдуть за конструкцією try. catch. Він виконується завжди, незалежно від того, був виняток чи ні.
Ви можете використовувати вкладені конструкції try. Якщо внутрішня конструкція try не має блоку catch (таке може бути при її використанні у вигляді try <. >finaly <. >, тому що try <. >не може бути без блоків catch або finally ), буде викликано сatch зовнішньої конструкції try .
Конструкція try також використовується для обробки винятків JavaScript (тобто викинутих внутрішніми функціями мови або парсером). Завітайте у JavaScript посібник для додаткової інформації про JavaScript винятки.
Безумовний блок catch
При використанні блоку catch він викликається для будь - якого виключення в блоці try . Наприклад, коли в наступному коді відбувається помилка, керування переходить до блоку catch .
Блок catch задає ідентифікатор (e у прикладі вище) який містить об'єкт виключення (у прикладі вище – значення, передане оператору throw). Область видимості цього об'єкта обмежується блоком catch.
Умовний блок catch
"Умовні блоки catch" можна створювати за допомогою try. catch з if. else if. else , як тут:
try < myroutine(); // може викидати три види винятків >catch (e) < if (e instanceof TypeError) < // обробка винятку TypeError >else if (e instanceof RangeError) < // обробка винятку RangeError >else if (e instanceof EvalError) < // обробка виключення EvalError >else < // обробка інших винятків logMyErrors(e); // передати обробнику помилок >>
Частий сценарій використання — обробити відомі винятки, а за невідомих помилок, прокинути їх далі:
Ідентифікатор виключення
Коли у блоці try викидається виняток, exception_var (тобто.e у конструкції catch (e) ) містить значення виключення. Його можна використовувати, щоб отримати більше інформації про викинутий виняток.
try < if (!firstValidation()) < throw 1; >if (!secondValidation()) < throw 2; e);
Блок finally
Блок finally містить код, який буде запущений після коду в блоках try і catch . catch чи ні. Блок finally можна використовувати для того, щоб скрипт безпечно завершив роботу у разі помилки Наприклад, якщо необхідно звільнити пам'ять та ресурси які використав скрипт.
Наявність спеціального блоку, пов'язаного з помилкою, який виконується незалежно від наявності виняткової ситуації, може здатися дивною, але ця конструкція насправді дуже корисна.
function expensiveCalculations() < // Складні обчислення >function maybeThrowError() < // Функція, яка може викинути виняток if (Math.random() >0.5) throw new Error(); > try < // Тепер при прокручуванні сторінки відбуватимуться // складні обчислення, що сильно позначиться // продуктивності window.addEventListener("scroll", expensiveCalculations); maybeThrowError(); >window.removeEventListener("scroll", expensiveCalculations);
У цьому прикладі, якщо функція maybeThrowError викине виняток усередині блоку try, управління перейде в блок catch. Якщо і в блоці catch ця функція теж викине виняток, то виконання коду перерветься, і обробник події не буде знято, доки користувач не перезавантажить сторінку, що погано позначиться на швидкості роботи. Щоб уникнути таких ситуацій, слід використовувати блок finally :
Інший приклад: робота із файлами. У наступному фрагменті коду показується, як скрипт відкриває файл та записує в нього якісь дані (у серверному оточенні JavaScript має доступ до файлової системи). Під час запису може виникнути помилка. Але після відкриття файл дуже важливо закрити, тому що незакритий файл може призвести до витоку пам'яті. У таких випадках використовується блок finally:
openMyFile(); try < // Зробити щось із файлом writeMyFile(theData); >finally < closeMyFile(); // Закрити файл, що б не сталося >
Приклади
Вкладені блоки try
Для початку давайте подивимося, що робить цей код:
try < try < throw new Error("упс"); >finally < console.log("finally"); >> catch(e) < console.error("зовнішній блок catch", e.message); >// Висновок: // "finally" // "Зовнішній блок catch" "упс"
Тепер відловимо виняток у внутрішньому блоці try, додавши до нього блок catch:
try < try < throw new Error("упс"); >catch (e) < console.error("внутрішній блок catch", e.message); >finally < console.log("finally"); >> catch(e) < console.error("зовнішній блок catch", e.message); >// Output: // "внутрішній блок catch" "упс" // "finally"
Нарешті, прокинемо помилку
try <try throw new Error("упс"); >catch (e) < console.error("внутрішній блок catch", e.message); throw e; >finally < console.log("finally"); >> catch(e) < console.error("зовнішній блок catch", e.message); >// Висновок: // "внутрішній блок catch" "oops" // "finally" // "Зовнішній блок catch" "oops"
Будь-який виняток буде передано тільки в найближчий блок catch, якщо він не прокине його далі.Усі винятки, викинутими внутрішніми блоками (бо код у блоці catch також може викинути виняток), будуть спіймані зовнішніми.
Повернення значення з блоку finally
Якщо блок finally повертає якесь значення, воно стає значенням, яке повертає вся конструкція try. catch. finally, незалежно від будь-яких інструкцій return в блоках try і catch. Також ігноруються винятки, викинуті блоком catch.
(() => < try < try < throw new Error("oops"); >catch (ex) < console.error("inner", ex.message); throw ex; >finally < console.log("finally "); return; >> catch (ex) < console.error("outer", ex.message); >>)(); // Logs: // "inner" "oops" // "finally"
"упс" не доходить до зовнішнього блоку через інструкцію return в блоці finally . Те саме відбудеться з будь-яким значенням, що повертається з блоку catch .
Специфікації
Сумісність із браузерами
BCD tables only load in the browser
