Вы должны «внедрить» некоторый код в функцию, которая запускается как новая процедура: вам нужно вызвать отложенную функцию, в которой вы вызываете recover()
.Это единственный способ оправиться от состояния паники.См. Связанный: Почему `defer recovery ()` не улавливает панику?
Например:
go func() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Caught:", r)
}
}()
panic("catch me")
}()
Это выдаст (попробуйте на Go Playground ):
Caught: catch me
Невозможно делать это на каждой запускаемой вами подпрограмме, но, конечно, вы можете переместить функцию восстановления-записи в именованную функцию и просто вызвать ее (ноконечно, отложено):
func main() {
go func() {
defer logger()
panic("catch me")
}()
time.Sleep(time.Second)
}
func logger() {
if r := recover(); r != nil {
fmt.Println("Caught:", r)
}
}
Это приведет к тому же результату (попробуйте на Go Playground ).
Еще одно, более удобное и еще более компактное решениесоздать вспомогательную функцию, «обертку», которая получает функцию и заботится о восстановлении.
Вот как это может выглядеть:
func wrap(f func()) {
defer func() {
if r := recover(); r != nil {
fmt.Println("Caught:", r)
}
}()
f()
}
И теперь, используя еееще проще:
go wrap(func() {
panic("catch me")
})
go wrap(func() {
panic("catch me too")
})
Будет выводиться (попробуйте на Go Playground ):
Caught: catch me
Caught: catch me too
Заключительная нота:
Обратите внимание, что запуск реальной программы происходит за пределами wrap()
.Это дает вызывающей стороне возможность решить, требуется ли новая процедура, просто добавив префикс wrap()
к go
.Обычно такой подход предпочтительнее в Go.Это позволяет вам выполнять произвольные функции, передавая их wrap()
, и он будет "защищать" свое выполнение (восстанавливаясь от паники, правильно регистрируя / сообщая об этом), даже если вы не хотите запускать его одновременно в новой программе.