Обробка помилок¶
У програмуванні обробка помилок - це складна тема з багатьма підводними каменями, і вона може бути ще складнішою, коли ви пишете код в межах обмежень фреймворку, оскільки спосіб обробки помилок має узгоджуватися зі способом диспетчеризації помилок фреймворком і навпаки.
У цій статті описано загальні риси обробки помилок фреймворком JavaScript та Owl, а також наведено деякі рекомендації щодо взаємодії з цими системами таким чином, щоб уникнути поширених проблем.
Помилки в JavaScript¶
Перш ніж ми заглибимося в те, як обробляються помилки в Odoo, а також як і де налаштувати поведінку обробки помилок, варто переконатися, що ми розуміємо, що саме маємо на увазі під словом «помилка», а також деякі особливості обробки помилок у JavaScript.
Клас Error¶
Перше, що може спадати на думку, коли ми говоримо про обробку помилок, це вбудований клас Error або класи, які його розширюють. Далі в цій статті, коли ми посилатимемося на об’єкт, який є екземпляром цього класу, ми використовуватимемо термін об’єкт Error, виділений курсивом.
Кинути можна все, що завгодно¶
У JavaScript можна викидати будь-яке значення. Зазвичай викидають об’єкти Error, але можна викидати будь-який інший об’єкт і навіть примітиви. Хоча ми не рекомендуємо викидати щось, що не є об’єктом Error, фреймворк Odoo JavaScript повинен вміти обробляти такі сценарії, що допоможе вам зрозуміти деякі дизайнерські рішення, які нам довелося прийняти.
Під час створення екземпляра об’єкт Error браузер збирає інформацію про поточний стан «стек викликів» (або належного стеку викликів, або реконструйованого стеку викликів для асинхронних функцій та продовжень обіцянок). Ця інформація називається «трасування стека» і дуже корисна для налагодження. Фреймворк Odoo відображає це трасування стека в діалогових вікнах помилок, коли воно доступне.
Під час видачі значення, яке не є об’єктом Error, браузер все одно збирає інформацію про поточний стек викликів, але ця інформація недоступна в JavaScript: вона доступна в консолі devtools лише якщо помилку не оброблено.
Викидання об’єктів Error дозволяє нам показувати детальнішу інформацію, яку користувач зможе скопіювати/вставити за потреби для звіту про помилку, але це також робить обробку помилок більш надійною, оскільки дозволяє нам фільтрувати помилки на основі їхнього класу під час їх обробки. На жаль, JavaScript не має синтаксичної підтримки для фільтрації за класом помилки в реченні catch, але ви можете відносно легко зробити це самостійно:
try {
doStuff();
} catch (e) {
if (!(e instanceof MyErrorClass)) {
throw e; // caught an error we can't handle, rethrow
}
// handle MyErrorClass
}
Відхилення обіцянок – це помилки¶
На ранніх етапах впровадження Promise, Promise часто розглядалися як спосіб зберігання непересічного об’єднання результату та «помилки», і було досить поширеним явищем використання відхилення Promise як способу сигналізації про м’яку помилку. Хоча на перший погляд це може здатися гарною ідеєю, браузери та середовища виконання JavaScript вже давно почали обробляти відхилені Promise так само, як і викинуті помилки практично в усіх відношеннях:
Виконання асинхронної функції має той самий ефект, що й повернення відхиленого Promise зі значенням, що було викинуто, як причиною відхилення.
блоки catch в асинхронних функціях catch відхилені обіцянки, які очікувалися у відповідному блоці try.
середовища виконання збирають інформацію про стек відхилених обіцянок (promise).
відхилений Promise, який не перехоплюється синхронно, надсилає подію на глобальний об’єкт/window, і якщо
preventDefaultне викликається для цієї події, браузери реєструють помилку, а автономні середовища виконання, такі як node, завершують процес.функція налагоджувача «пауза на винятках» призупиняє виконання обіцянок, коли їх відхиляють
З цих причин фреймворк Odoo обробляє відхилені Promises так само, як і викинуті помилки. Не створюйте відхилені promises там, де ви не викинете помилку, і завжди відхиляйте Promises, вказуючи об’єкти помилок як причину відхилення.
Події error не є помилками¶
За винятком подій error у вікні, події error на інших об’єктах, таких як елементи <media>, <audio>, <img>, <script> та <link>, або об’єкти XMLHttpRequest, не є помилками. У цій статті термін «error» стосується лише викинутих значень та відхилених обіцянок. Якщо вам потрібно обробляти помилки на цих елементах або ви хочете, щоб вони розглядалися як помилки, вам потрібно явно додати слухач подій для цієї події:
const scriptEl = document.createElement("script");
scriptEl.src = "https://example.com/third_party_script.js";
return new Promise((resolve, reject) => {
scriptEl.addEventListener("error", reject);
scriptEl.addEventListener("load", resolve);
document.head.append(scriptEl);
});
Життєвий цикл помилок у фреймворку Odoo JS¶
Викинуті помилки розмотують свій стек викликів, щоб знайти блок catch, який може їх обробити. Спосіб обробки помилки залежить від того, який код зустрічається під час розмотування стеку викликів. Хоча існує практично нескінченна кількість місць, звідки можуть виникати помилки, існує лише кілька можливих шляхів до коду обробки помилок JS-фреймворку.
Виклик помилки на верхньому рівні модуля¶
Коли завантажується JS-модуль, код верхнього рівня цього модуля виконується та може призвести до виникнення помилки. Хоча фреймворк може повідомляти про ці помилки за допомогою діалогового вікна, завантаження модуля є критичним моментом для фреймворку JavaScript, і деякі модулі, що викликають помилки, можуть перешкоджати повному завантаженню коду фреймворку, тому будь-яке повідомлення про помилки на цьому етапі є «найкращими зусиллями». Помилки, що виникають під час завантаження модуля, повинні, як мінімум, реєструвати повідомлення про помилку в консолі браузера. Оскільки цей тип помилки є критичним, програма не може її відновити, і вам слід написати свій код таким чином, щоб модуль не міг викинути помилку під час визначення. Будь-яка обробка та звітування про помилки, що відбувається на цьому етапі, призначена виключно для того, щоб допомогти вам, розробнику, виправити код, який викликав помилку, і ми не надаємо жодного механізму для налаштування способу обробки цих помилок.
Служба помилок¶
Коли виникає помилка, але вона не перехоплюється, середовище виконання надсилає подію глобальному об’єкту (window). Тип події залежить від того, чи була помилка викинута синхронно чи асинхронно: синхронно викинуті помилки надсилають подію error, а помилки, викинуті в асинхронному контексті, а також відхилені проміси, надсилають подію unhandledrejection.
JS-фреймворк містить сервіс, призначений для обробки цих подій: сервіс помилок. При отриманні однієї з цих подій сервіс помилок починає зі створення нового об’єкта Error, який використовується для обгортання викинутої помилки; це тому, що може бути викинуто будь-яке значення, а Promises можуть бути відхилені з будь-яким значенням, включаючи undefined або null, що означає, що не гарантується, що він містить будь-яку інформацію або що ми можемо зберігати будь-яку інформацію про це значення. Обгортаючий об’єкт Error використовується для збору певної інформації про викинуте значення, щоб його можна було рівномірно використовувати в коді фреймворку, який має відображати інформацію про помилки будь-якого типу.
Служба помилок зберігає повну трасування стека викинутої помилки на цьому обгортковому об’єкті Error і, коли режим налагодження - assets, використовує карти вихідного коду, щоб додати інформацію до цієї трасування стеку про вихідний файл, який містить функцію кожного фрейму стеку. Позиція функції у зв’язаних ресурсах зберігається, оскільки це може бути корисним у деяких сценаріях. Коли помилки мають причину, цей процес також розмотує ланцюжок причини для створення повної складеної трасування стеку. Хоча поле причина в об’єктах Error є стандартним, деякі основні браузери все ще не відображають повну трасування стеку ланцюжків помилок. Через це ми додаємо цю інформацію вручну. Це особливо корисно, коли помилки викидаються всередині хуків Owl, про це пізніше.
Щойно помилка-обгортка містить всю необхідну інформацію, ми починаємо процес фактичної обробки помилки. Для цього служба помилок послідовно викликає всі функції, зареєстровані в реєстрі error_handlers, доки одна з цих функцій не поверне істинне значення, яке сигналізує про обробку помилки. Після цього, якщо preventDefault не було викликано під час події помилки, і якщо служба помилок змогла додати трасування стека до об’єкта помилки-обгортки, служба помилок викликає preventDefault під час події помилки та реєструє трасування стека в консолі. Це пояснюється тим, що, як згадувалося раніше, деякі браузери неправильно відображають ланцюжки помилок, і поведінка події за замовчуванням полягає в тому, що браузер реєструє помилку, тому ми просто перевизначаємо цю поведінку, щоб реєструвати повніше трасування стека. Якщо служба помилок не змогла зібрати інформацію про трасування стека про викинуту помилку, ми не викликаємо preventDefault. Це може статися під час викидання значень, що не є помилками: рядків, невизначених або інших випадкових об’єктів. У таких випадках браузер сам реєструє трасування стека, оскільки він має цю інформацію, але не надає її JS-коду.
Реєстр error_handlers¶
Реєстр error_handlers - це основний спосіб розширення способу обробки JS-фреймворком «загальних» помилок. Загальні помилки в цьому контексті означають помилки, які можуть виникати в багатьох місцях, але які слід обробляти однаково. Деякі приклади:
UserError: коли користувач намагається виконати операцію, яку код Python вважає недійсною з бізнес-причин, код Python викликає UserError, а функція rpc викидає відповідну помилку в JavaScript. Це може статися на будь-якому rpc будь-де, і ми не хочемо, щоб розробникам доводилося явно обробляти цей тип помилок у всіх цих місцях, і ми хочемо, щоб однакова поведінка відбувалася всюди: зупинити поточний виконуваний код (що досягається за допомогою throw), і відобразити діалогове вікно, яке пояснює користувачеві, що пішло не так.
AccessError: те саме міркування, що й для помилок користувача: це може статися будь-коли та має відображатися однаково незалежно від того, де це сталося
LostConnection: знову ті ж міркування.
Виклик помилки в компоненті Owl¶
Реєстрація або зміна компонентів Owl – це основний спосіб розширення функціональності веб-клієнта. Таким чином, більшість помилок, що виникають, так чи інакше викликаються компонентом Owl. Існує кілька можливих сценаріїв:
Додавання в налаштування компонента або під час рендерингу
Викидання зсередини hook життєвого циклу
Виклик з обробника подій
Викидання помилки обробником подій або функцією чи методом, викликаним безпосередньо чи опосередковано з обробника подій, означає, що ні код Owl, ні код JS-фреймворку не знаходяться в стеку викликів. Якщо помилку не перехопити, вона потрапляє безпосередньо в службу помилок.
Викидання помилки обробника подій або цією функцією методом, викликаним випадком чи опосередковано з обробником подій, означає, що жоден код Owl, жоден код JS-фреймворка не знайдено у стеці викликів. Якщо помилку не перехопити, вона йде разом у службу помилок.
Перегляньте також
Всередині Odoo є місця, де ми не хочемо, щоб уся програма аварійно завершила роботу у разі помилки, тому фреймворк має кілька місць, де використовується гачок onError. Служба дій обгортає дії та представлення в компонент, який обробляє помилки. Якщо клієнтська дія або представлення видає помилку під час рендерингу, вона намагається повернутися до попередньої дії. Помилка надсилається до служби помилок, щоб діалогове вікно помилки можна було відобразити незалежно від цього. Подібна стратегія використовується в більшості місць, де фреймворк викликає код «користувача»: ми зазвичай припиняємо відображення несправного компонента та показуємо діалогове вікно помилки.
Під час викидання помилки всередині функції зворотного виклику хука, Owl створює новий об’єкт Error, який містить інформацію стеку про те, де був зареєстрований hook, і встановлює його причину як початково викинуте значення. Це пояснюється тим, що трасування стека початкової помилки не містить інформації про те, який компонент зареєстрував цей хук і де, вона містить лише інформацію про те, що викликало хук. Оскільки хуки викликаються кодом Owl, більшість цієї інформації зазвичай не дуже корисна для розробників, але знання того, де був зареєстрований хук і яким компонентом, дуже корисне.
Під час читання помилок, у яких згадується «OwlError: у <hookName> виникла наступна помилка», обов’язково прочитайте обидві частини складеної трасування стека:
Error: The following error occurred in onMounted: "My error"
at wrapError
at onMounted
at MyComponent.setup
at new ComponentNode
at Root.template
at MountFiber._render
at MountFiber.render
at ComponentNode.initiateRender
Caused by: Error: My error
at ParentComponent.someMethod
at MountFiber.complete
at Scheduler.processFiber
at Scheduler.processTasks
Перший виділений рядок повідомляє, який компонент зареєстрував hook onMounted, тоді як другий виділений рядок повідомляє, яка функція викликала помилку. У цьому випадку дочірній компонент викликає функцію, яку він отримав як властивість від свого батьківського компонента, і ця функція є методом батьківського компонента. Обидві інформації можуть бути корисними, оскільки метод міг бути викликаний помилково дочірнім компонентом (або в той момент життєвого циклу, де не слід було цього робити), але також може бути, що батьківський метод містить помилку.
Позначення помилок як оброблених¶
У попередніх розділах ми обговорювали два способи реєстрації обробників помилок: один - додавання їх до реєстру error_handlers, інший - використання хука onError в owl. В обох випадках обробник має вирішити, чи позначати помилку як оброблену.
onError¶
У випадку обробника, зареєстрованого в Owl з onError, помилка вважається Owl обробленою, якщо ви не викличете її повторно. Що б ви не робили в onError, інтерфейс користувача, ймовірно, не синхронізований зі станом програми, оскільки помилка завадила owl виконати певну роботу. Якщо ви не можете обробити помилку, вам слід викинути її повторно та дозволити решті коду обробити її.
Якщо ви не повторно викинете помилку, вам потрібно змінити певний стан, щоб програма могла знову відображатися без помилок. На цьому етапі, якщо ви не повторно викинете помилку, про неї не буде повідомлено. У деяких випадках це бажано, але в більшості випадків вам слід натомість відправити цю помилку в окремий стек викликів поза Owl. Найпростіший спосіб зробити це – просто створити відхилений Promise з помилкою як причиною відхилення:
import { Component, onError } from "@odoo/owl";
class MyComponent extends Component {
setup() {
onError((error) => {
// implementation of this method is left as an exercise for the reader
this.removeErroringSubcomponent();
Promise.reject(error); // create a rejected Promise without passing it anywhere
});
}
}
Це змушує браузер надсилати подію unhandledrejection для вікна, що призводить до активації обробки помилок JS-фреймворку та їх обробки, у більшості випадків шляхом відкриття діалогового вікна з інформацією про помилку. Це стратегія, яка використовується внутрішньо сервісом дій та сервісом діалогів, щоб зупинити відображення непрацюючих дій або діалогів, водночас повідомляючи про помилку.
Обробник у реєстрі error_handlers¶
Обробники, додані до реєстру error_handlers, можуть позначати помилку як оброблену двома способами з різними значеннями.
Це змушує браузер надсилати подію unhandledrejection для вікна, що призводить до активації обробки помилок JS-фреймворку та їх обробки, у більшості випадків шляхом відкриття діалогового вікна з інформацією про помилку. Це стратегія, яка використовується внутрішньо сервісом дій та сервісом діалогів, щоб зупинити відображення непрацюючих дій або діалогів, водночас повідомляючи про помилку.
Інший спосіб – викликати preventDefault під час події помилки: це має інше значення. Після визначення можливості обробки помилки обробник повинен вирішити, чи дозволено отримувати отриману помилку під час нормальної роботи, і якщо так, то слід викликати preventDefault. Це зазвичай застосовується до бізнес-помилок, таких як помилки доступу або помилки перевірки: користувачі можуть ділитися посиланнями з іншими користувачами на ресурси, до яких вони не мають доступу, а також можуть спробувати зберегти запис, який перебуває в недійсному стані.
Якщо не викликається preventDefault, помилка вважається неочікуваною, будь-яке таке виникнення під час тесту призводить до його невдалого завершення, оскільки зазвичай це свідчить про дефектний код.
Уникайте помилок, наскільки це можливо¶
Помилки створюють складність у багатьох аспектах, ось кілька причин, чому їх слід уникати.
Помилки дорого коштують¶
Оскільки помилки повинні розмотувати стек викликів та збирати інформацію під час їх виконання, викидання помилок відбувається повільно. Крім того, середовища виконання JavaScript зазвичай оптимізовані з припущенням, що винятки трапляються рідко, і тому код зазвичай компілюється з припущенням, що винятки не викидаються, і повертається до повільнішого шляху коду, якщо це коли-небудь станеться.
Викидання помилок ускладнює налагодження¶
Налагоджувачі JavaScript, такі як, наприклад, той, що входить до складу інструментів розробника Chrome та Firefox, мають функцію, яка дозволяє призупиняти виконання, коли виникає виняток. Ви також можете вибрати, чи призупиняти лише перехоплені винятки, чи як перехоплені, так і неперехоплені винятки.
Коли ви викидаєте помилку всередині коду, який викликається Owl або фреймворком JavaScript (наприклад, у полі, поданні, дії, компоненті тощо), оскільки вони керують ресурсами, їм потрібно виявляти помилки та перевіряти їх, щоб вирішити, чи є помилка критичною, і програма повинна аварійно завершити роботу, чи помилка очікувана і її слід обробляти певним чином.
Через це майже всі помилки, що виникають у коді JavaScript, у певний момент перехоплюються, і хоча вони можуть бути перевикинуті, якщо їх неможливо обробити, це означає, що використання функції «пауза на неперехоплених винятках» фактично марне під час роботи в Odoo, оскільки вона завжди призупиняється в коді фреймворку JavaScript, а не біля коду, який спочатку викинув помилку.
Однак, функція «пауза на перехоплених винятках» все ще дуже корисна, оскільки вона призупиняє виконання для кожного оператора throw та відхиленої обіцянки. Це дозволяє розробнику зупиняти та перевіряти контекст виконання щоразу, коли виникає виняткова ситуація.
Однак, це вірно лише за умови, що винятки викидаються рідко. Якщо винятки викидаються регулярно, будь-яка дія на сторінці може призвести до зупинки виконання налагоджувачем, і розробнику, можливо, доведеться пройти через багато «звичайних» винятків, перш ніж він зможе дійти до фактичного виняткового сценарію, який його цікавить. У деяких ситуаціях, оскільки натискання кнопки відтворення в налагоджувачі видаляє фокус зі сторінки, це може навіть зробити цікавий сценарій викиду недоступним без використання комбінації клавіш для відновлення виконання, що призводить до погіршення досвіду розробника.
Викидання порушує нормальний хід коду¶
Під час виникнення помилки код, який виглядає так, ніби має виконуватися завжди, може бути пропущений, що може спричинити багато малопомітних помилок та витоків пам’яті. Ось простий приклад:
eventTarget.addEventListener("event", handler);
someFunction();
eventTarget.removeEventListener("event", handler);
У цьому блоці коду ми додаємо слухач подій до цільової події, а потім викликаємо функцію, яка може надсилати події для цієї цілі. Після виклику функції ми видаляємо слухач подій.
Якщо someFunction викликає виклик, слухач події ніколи не буде видалено. Це означає, що пам’ять, пов’язана з цим слухачем події, фактично витікає і ніколи не буде звільнена, доки сам eventTarget не буде звільнено.
Окрім витоку пам’яті, те, що обробник все ще підключений, означає, що його можуть викликати для подій, що відправляються з причин, відмінних від виклику someFunction. Це помилка.
Щоб врахувати це, потрібно було б обгорнути виклик у блок try, а очищення - у блок finally:
eventTarget.addEventListener("event", handler);
try {
someFunction();
} finally {
eventTarget.removeEventListener("event", handler);
}
Хоча це дозволяє уникнути вищезгаданих проблем, це не лише вимагає більше коду, але й потребує знання того, що може викинути функція. Було б неможливо обгорнути весь код, який може викинути блок try/finally.
Виявлення помилок¶
Іноді вам потрібно викликати код, який, як відомо, викидає помилки, і ви хочете обробити деякі з цих помилок. Є дві важливі речі, які слід пам’ятати:
Повторно вивести помилки, які не є очікуваним типом помилок. Зазвичай це слід робити за допомогою перевірки
instanceof.Тримайте блок try якомога меншим. Це дозволить уникнути перехоплення помилок, які не є тими, що ви намагаєтеся перехопити. Зазвичай блок try повинен містити рівно один оператор.
let someVal;
try {
someVal = someFunction();
// do not start working with someVal here.
} catch (e) {
if (!(e instanceof MyError)) {
throw e;
}
someVal = null;
}
// start working with someVal here
Хоча з try/catch це просто, набагато легше випадково обгорнути набагато більшу частину коду в речення catch під час роботи з Promise.catch:
someFunction().then((someVal) => {
// work with someVal
}).catch((e) => {
if (!(e instanceof MyError)) {
throw e;
}
return null;
});
У цьому прикладі блок catch фактично перехоплює помилки у всьому блоці then, чого ми не хочемо. У цьому конкретному прикладі, оскільки ми належним чином фільтруємо на основі типу помилки, ми не «ковтаємо» помилку, але ви можете бачити, що це може бути набагато простіше зробити, якщо ми очікуємо один тип помилки та вирішуємо не перевіряти instanceof. Однак зауважте, що на відміну від попереднього прикладу, значення null не проходить через кодовий шлях, який використовує someVal. Щоб уникнути цього, речення catch, як правило, повинні бути якомога ближче до обіцянки, яка може викликати помилку, і завжди повинні фільтрувати за типом помилки.
Безпомилковий потік керування¶
З причин, зазначених вище, слід уникати викидання помилок для виконання рутинних дій, зокрема, для потоку керування. Якщо очікується, що функція не зможе регулярно виконувати свою роботу, вона повинна повідомляти про цю помилку, не викидаючи виняток. Розглянемо приклад коду:
let someVal;
try {
someVal = someFunction();
} catch (e) {
if (!(e instanceof MyError)) {
throw e;
}
someVal = null;
}
У цьому коді є багато проблем. По-перше, оскільки ми хочемо, щоб змінна someVal була доступна після блоку try/catch, її потрібно оголосити перед цим блоком, і вона не може бути const, оскільки її потрібно призначити після ініціалізації. Це погіршує читабельність у майбутньому, оскільки тепер вам доведеться стежити за тим, щоб ця змінна не була перепризначена пізніше в коді.
По-друге, коли ми виявляємо помилку, нам потрібно перевірити, чи це дійсно той тип помилки, який ми очікували, і якщо ні, повторно викинути помилку. Якщо ми цього не зробимо, ми можемо отримати помилки, які насправді були неочікуваними, замість того, щоб правильно про них повідомляти, наприклад, ми можемо перехоплювати та приймати TypeError, якщо базовий код намагається отримати доступ до властивості на null або undefined.
Зрештою, це не тільки дуже багатослівно, але й легко зробити неправильно: якщо ви забудете додати try/catch, ви, ймовірно, отримаєте трасування. Якщо ви додасте блок try/catch, але забудете повторно викинути неочікувані помилки, ви проковтнете непов’язані помилки. А якщо ви хочете уникнути необхідності перепризначення змінної, ви можете перемістити весь блок, який використовує змінну, всередину блоку try. Чим більше коду у вас всередині блоку try, тим більша ймовірність того, що ви перехопите непов’язані помилки та проковтнете їх, якщо ви забули фільтрувати за типом помилки. Це також додає рівень відступу до всього блоку, і ви навіть можете отримати вкладені блоки try/catch. Зрештою, це ускладнює визначення, який рядок насправді має викинути помилку.
У наступних розділах описано деякі альтернативні підходи, які ви можете використовувати замість використання помилок.
Повертає null або undefined¶
Якщо функція повертає примітив або об’єкт, зазвичай можна використовувати null або undefined, щоб сигналізувати про те, що вона не змогла виконати свою роботу. У більшості випадків цього достатньо. Код у результаті виглядає приблизно так:
const someVal = someFunction();
// further
if (someVal !== null) { /* do something */ }
Як бачите, це набагато простіше.
Повертає об’єкт або масив¶
У деяких випадках значення null або undefined є частиною очікуваних повернених значень. У таких випадках ви можете замість цього повернути об’єкт-обгортку або масив із двох елементів, який містить або повернене значення, або помилку:
const { val: someVal, err } = someFunction();
if (err) {
return;
}
// do something with someVal as it is known to be valid
Або за допомогою масиву:
const [err, someVal] = someFunction();
if (err) {
return;
}
// do something with someVal as it is known to be valid
Примітка
Під час використання двоелементного масиву бажано, щоб помилка була першим елементом, щоб її було важче помилково ігнорувати під час деструктуризації. Потрібно явно додати заповнювач або кому, щоб пропустити помилку, тоді як якщо помилка є другим елементом, легко просто деструктуризувати лише перший елемент і помилково забути обробити помилку.
Коли викидати помилки¶
У попередніх розділах наведено багато вагомих причин уникати помилок, тож які приклади випадків, коли помилка є найкращим варіантом дій?
Загальні помилки, які можуть траплятися в багатьох місцях, але повинні розглядатися однаково скрізь; наприклад, помилки доступу можуть траплятися практично в будь-якому RPC, і ми завжди хочемо відображати інформацію про те, чому користувач не має доступу.
Деяка передумова, яка завжди має бути виконана для певної операції, не виконується; наприклад, представлення не вдалося відобразити, оскільки домен недійсний. Ці типи помилок, як правило, не призначені для перехоплення деінде та сигналізують про те, що код неправильний або дані пошкоджені. Викидання помилки змушує фреймворк аварійно завершити роботу та запобігає роботі в несправному стані.
Під час рекурсивного обходу глибокої структури даних, викидання помилки може бути більш ергономічним та менш схильним до помилок, ніж ручна перевірка на наявність помилок та пересилання їх через багато рівнів викликів. На практиці це мало б траплятися дуже рідко, і його потрібно зважити з усіма недоліками, згаданими в цій статті.