HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-03-10
1 <p>Хорошо ли будет, если при вводе неверных данных в форму возникнет внутренняя ошибка сервера "500"? Да, когда происходит что-либо непредвиденное, нас учат генерировать исключения, однако практика показывает, что это не самый лучший метод обработки ошибок. Давайте разберемся, почему. Заодно и поговорим, как управлять потоком выполнения с исключениями.</p>
1 <p>Хорошо ли будет, если при вводе неверных данных в форму возникнет внутренняя ошибка сервера "500"? Да, когда происходит что-либо непредвиденное, нас учат генерировать исключения, однако практика показывает, что это не самый лучший метод обработки ошибок. Давайте разберемся, почему. Заодно и поговорим, как управлять потоком выполнения с исключениями.</p>
2 <h3>Исключения нарушают безопасность типов</h3>
2 <h3>Исключения нарушают безопасность типов</h3>
3 <p>Это так, и это происходит даже в статически типизированных языках. Дело в том, что согласно своей сигнатуре, функция fetchUser(id: number): User обязана вернуть пользователя. И ничего в сигнатуре функции не говорит нам о том, что если пользователь найден не будет, будет сгенерировано исключение. А если исключение ожидается, то более подходящей сигнатурой будет: fetchUser(...): User|throws UserNotFoundError. Но такой синтаксис уже недопустим, причем вне зависимости от языка.</p>
3 <p>Это так, и это происходит даже в статически типизированных языках. Дело в том, что согласно своей сигнатуре, функция fetchUser(id: number): User обязана вернуть пользователя. И ничего в сигнатуре функции не говорит нам о том, что если пользователь найден не будет, будет сгенерировано исключение. А если исключение ожидается, то более подходящей сигнатурой будет: fetchUser(...): User|throws UserNotFoundError. Но такой синтаксис уже недопустим, причем вне зависимости от языка.</p>
4 <p>Анализ программ, генерирующих исключения, становится сложным. Собственно говоря, никто и никогда не знает, а будет ли функция генерировать это самое исключение. Да, мы можем обернуть каждый вызов функции в блок try/catch, однако это является непрактичным, да и существенно ухудшает читаемость кода.</p>
4 <p>Анализ программ, генерирующих исключения, становится сложным. Собственно говоря, никто и никогда не знает, а будет ли функция генерировать это самое исключение. Да, мы можем обернуть каждый вызов функции в блок try/catch, однако это является непрактичным, да и существенно ухудшает читаемость кода.</p>
5 <h3>Исключения становятся причиной нарушения композиции функций</h3>
5 <h3>Исключения становятся причиной нарушения композиции функций</h3>
6 <p>Исключения делают практически невозможным применение композиции функций. В примере ниже сервер вернет нам внутреннюю ошибку ("500"), если одна из публикаций не найдена в блоге.</p>
6 <p>Исключения делают практически невозможным применение композиции функций. В примере ниже сервер вернет нам внутреннюю ошибку ("500"), если одна из публикаций не найдена в блоге.</p>
7 <p>Но что, если одно из сообщений будет удалено, а пользователь будет пытается получить доступ к сообщению из-за какого-нибудь бага? Все это существенно ухудшит User Experience.</p>
7 <p>Но что, если одно из сообщений будет удалено, а пользователь будет пытается получить доступ к сообщению из-за какого-нибудь бага? Все это существенно ухудшит User Experience.</p>
8 <h3>Кортежи - альтернативный способ обработки ошибок</h3>
8 <h3>Кортежи - альтернативный способ обработки ошибок</h3>
9 <p>Один из способов обработки ошибок заключается в возврате кортежа, который содержит результат и ошибку, вместо генерирования исключения. Язык программирования JavaScript кортежи не поддерживает, однако их можно легко эмулировать, применяя для этого массив из 2-х значений в форме [error, result]. Кстати говоря, это еще и стандартный метод обработки ошибок в Go:</p>
9 <p>Один из способов обработки ошибок заключается в возврате кортежа, который содержит результат и ошибку, вместо генерирования исключения. Язык программирования JavaScript кортежи не поддерживает, однако их можно легко эмулировать, применяя для этого массив из 2-х значений в форме [error, result]. Кстати говоря, это еще и стандартный метод обработки ошибок в Go:</p>
10 <h3>В некоторых случаях исключения хороши</h3>
10 <h3>В некоторых случаях исключения хороши</h3>
11 <p>Нельзя не сказать, что исключения продолжают занимать определенное место в вашей кодовой базе. Спросите себя, желаете ли вы, чтобы ваша программа завершилась аварийно? Ведь любое брошенное исключение способно "уронить" весь процесс. И пусть даже вы предполагаете, что пристально изучили все потенциальные пограничные случаи, все же стоит понимать, что исключения небезопасны и станут причиной аварийного завершения программы в будущем. Следует выбрасывать исключения лишь тогда, когда вы абсолютно уверены и имеете намерение вывести программу из строя. К примеру, если речь идет об ошибке разработчика либо о сбое соединения с БД.</p>
11 <p>Нельзя не сказать, что исключения продолжают занимать определенное место в вашей кодовой базе. Спросите себя, желаете ли вы, чтобы ваша программа завершилась аварийно? Ведь любое брошенное исключение способно "уронить" весь процесс. И пусть даже вы предполагаете, что пристально изучили все потенциальные пограничные случаи, все же стоит понимать, что исключения небезопасны и станут причиной аварийного завершения программы в будущем. Следует выбрасывать исключения лишь тогда, когда вы абсолютно уверены и имеете намерение вывести программу из строя. К примеру, если речь идет об ошибке разработчика либо о сбое соединения с БД.</p>
12 <p>Собственно говоря, сам термин "исключение" говорит сам за себя и предполагает исключительные случаи применения, когда у программы попросту отсутствует иной выбор, кроме аварийного завершения. Что бы кто не говорил, бросать и перехватывать исключения -- вряд ли хороший способ контроля потока выполнения. Именно поэтому прибегать к выбрасыванию исключений надо лишь в случае неисправимых ошибок. Кстати говоря, неверный пользовательский ввод, о котором мы говорили в самом начале статьи, к таковым ошибкам не относится.</p>
12 <p>Собственно говоря, сам термин "исключение" говорит сам за себя и предполагает исключительные случаи применения, когда у программы попросту отсутствует иной выбор, кроме аварийного завершения. Что бы кто не говорил, бросать и перехватывать исключения -- вряд ли хороший способ контроля потока выполнения. Именно поэтому прибегать к выбрасыванию исключений надо лишь в случае неисправимых ошибок. Кстати говоря, неверный пользовательский ввод, о котором мы говорили в самом начале статьи, к таковым ошибкам не относится.</p>
13 <h3>Избегайте перехвата исключения - позвольте коду завершиться аварийно</h3>
13 <h3>Избегайте перехвата исключения - позвольте коду завершиться аварийно</h3>
14 <p>Избегать перехвата исключений - окончательное правило обработки ошибок. Да, можно выдавать ошибки, если разработчик намерен аварийно завершить работу программы. Но разработчик никогда не должен отлавливать такие ошибки. По сути, речь сейчас идет о подходе, который рекомендуется в таких функциональных языках, как Haskell и Elixir.</p>
14 <p>Избегать перехвата исключений - окончательное правило обработки ошибок. Да, можно выдавать ошибки, если разработчик намерен аварийно завершить работу программы. Но разработчик никогда не должен отлавливать такие ошибки. По сути, речь сейчас идет о подходе, который рекомендуется в таких функциональных языках, как Haskell и Elixir.</p>
15 <p>Единственное исключение из вышеописанного правила - использование сторонних API. И даже в этом случае лучше применять вспомогательную функцию, оборачивающую основную функцию и возвращающую кортеж [error, result]. В частности, для этого можно использовать такие инструменты, как, например, Saferr.</p>
15 <p>Единственное исключение из вышеописанного правила - использование сторонних API. И даже в этом случае лучше применять вспомогательную функцию, оборачивающую основную функцию и возвращающую кортеж [error, result]. В частности, для этого можно использовать такие инструменты, как, например, Saferr.</p>
16 <p>Стоит в очередной раз спросить себя - а кто вообще несет ответственность за ошибку? Пользователь? Тогда ошибку надо обработать изящно. То есть пользователю лучше показать красивое сообщение, а не внутреннюю ошибку "500".</p>
16 <p>Стоит в очередной раз спросить себя - а кто вообще несет ответственность за ошибку? Пользователь? Тогда ошибку надо обработать изящно. То есть пользователю лучше показать красивое сообщение, а не внутреннюю ошибку "500".</p>
17 <p>Что касается статического анализатора ESLint, то в нем отсутствует правило no-try-catch. При этом его ближайший сосед - no-throw.</p>
17 <p>Что касается статического анализатора ESLint, то в нем отсутствует правило no-try-catch. При этом его ближайший сосед - no-throw.</p>
18 <p>Таким образом, следует убедиться, что вы отбрасываете ошибки ответственно, то есть в исключительных случаях и когда ожидаете сбоя программы.</p>
18 <p>Таким образом, следует убедиться, что вы отбрасываете ошибки ответственно, то есть в исключительных случаях и когда ожидаете сбоя программы.</p>
19 <p>Предлагаемая конфигурация ESLint:</p>
19 <p>Предлагаемая конфигурация ESLint:</p>
20 <p><em><a>Источник</a></em></p>
20 <p><em><a>Источник</a></em></p>
21  
21