#14 golang panic Восстановление после паники - "перехват" исключетельной ситуации
Primary tabs
Forums:
Если на каком-то уровне стэка вызовов функций в нашей программе была выброшена паника, то мы можем перехватить её (или как говорят "восстановиться") с помощью определения отложенного выполнения обработчика через defer.
Рассмотрим пример:
func main() {
myFunc()
}
func myFunc() string {
fmt.Println("Тело функции")
defer func() {
// Проверим была ли ошибка
if err := recover(); err != nil {
// если была, то обработаем:
fmt.Println("Мы упали с сообщением:",
err)
}
}()
fmt.Println("Полезная работа")
// вызываем падение:
panic("Пора отдыхать!")
return "Есть результат!"
}
-- выполнив это код, получим распечатку:
Тело функции Полезная работа Мы упали с сообщением: Пора отдыхать!
-- в примере выше мы использовали блок if с инициаллизацией, где, собственно, с помощью вызова recover() останавливаем падение программы и получаем информацию о брошенной ранее панике.
Затем мы проверяем значение ошибки на непустоту, и если оно непусто, то выводим текстовое сообщение полученной из данных об ошибке (панике).
Как это работает
Паника останавливает выполнение и начинает раскручивать стэк вызовов в обратную сторону, с тем уточнением, что назначенная отложенная функция все-таки вызывается, даже в состоянии паники (что и иллюстрирует пример выше).
Как не надо использовать паники
Наш перехват паники очень похож на механиз исключений, не следует использовать панику и восстановление после паники как эмуляцию блока try-catch.
В golang паники ловят только для того, чтобы избежать падение горутины, которая должна обрабатывать еще и другие запросы, кроме текущего, который завершился ошибкой, но не для управления логикой.
Так, самим панику следует выбрасывать только когда всё действительно плохо (для так называемых "неустранимых ошибок"), а в обычных ситуациях, для ожидаемых проблем напр. некорретного пользовательского ввода просто возвращать признак ошибки, напр. как второе значение функции.
Паника в функции восстановления
Функция восстановления может и сама бросить панику, но следующая по стеку отложенная функция (если она есть, помним, что они складываются в стэк) может перехватить и её:
func main() {
myFunc()
}
func myFunc() {
defer func() {
if err := recover(); err != nil {
fmt.Println("Обработчик 1:", err)
}
}()
defer func() {
if err := recover(); err != nil {
fmt.Println("Обработчик 2:", err)
panic("Вторая паника!")
}
}()
fmt.Println("Some userful work")
panic("Первая паника!")
return
}
-- но вообще: бросать панику в восстановлении — плохая практика и подобный код лучше не писать, по тем же соображениям, что приведены выше (паники не должны служить для управления ожидаемой логикой работы программы)
Отдельный обработчик паник
Обработчик паники можно вынести в отдельную функцию и подключать в любой функции, где мы не хотим упасть из-за паники, а хотим обработать ошибку и восстановить выполнение, например для main():
import (
"fmt"
)
// Обработчик паник
func panicHandler() {
// Проверим была ли ошибка
if err := recover(); err != nil {
// если была, то обработаем:
fmt.Println(
"Мы упали c сообщением: \n",
err)
}
}
func main() {
// отложенная функция восстановления
// для обработки паники
defer panicHandler()
// самостоятельно вызываем критическую ошибку:
panic("Демонстрационная паника!")
}
-- если запустим это код, получим:
Мы упали c сообщением: Демонстрационная паника!
Видео-материалы
- : ВкВидео | Ютуб | Телеграм
- Log in to post comments
- 55 reads