golang学习笔记—— defer、panic 和 recover 处理错误
然后,程序会崩溃并记录日志消息。有时,你可能想要避免程序崩溃,改为在内部报告错误。异常的使用场景简单描述:Go中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理。通常情况下,当你想要避免忘记任务(例如关闭文件或运行清理进程)时,可以推迟某个函数的运行。通常,你会发送一条错误消息,说明为什么会进入紧急状态。Golang 没有结构化异常,使用 panic 抛
defer、panic 和 recover函数
defer 函数
在 Go 中,defer
语句会推迟函数(包括任何参数)的运行,直到包含 defer
语句的函数完成。 通常情况下,当你想要避免忘记任务(例如关闭文件或运行清理进程)时,可以推迟某个函数的运行。
可以根据需要推迟任意多个函数。 defer
语句按逆序运行,先运行最后一个,最后运行第一个。
panic 函数
运行时错误会使 Go 程序崩溃,例如尝试通过使用超出范围的索引或取消引用 nil 指针来访问数组。 你也可以强制程序崩溃。
内置 panic()
函数可以停止 Go 程序中的正常控制流。 当你使用 panic
调用时,任何延迟的函数调用都将正常运行。 进程会在堆栈中继续,直到所有函数都返回。 然后,程序会崩溃并记录日志消息。 此消息包含错误信息和堆栈跟踪,有助于诊断问题的根本原因。
调用 panic()
函数时,可以添加任何值作为参数。 通常,你会发送一条错误消息,说明为什么会进入紧急状态。
recover 函数
有时,你可能想要避免程序崩溃,改为在内部报告错误。 或者,你可能想要先清理混乱情况,然后再让程序崩溃。 例如,你可能想要关闭与某个资源的连接,以免出现更多问题。
Go 提供内置 recover()
函数,让你可以在程序崩溃之后重新获得控制权。 你只会在你同时调用 defer
的函数中调用 recover
。 如果调用 recover()
函数,则在正常运行的情况下,它会返回 nil
,没有任何其他作用。
异常处理
Golang 没有结构化异常,使用 panic 抛出错误,recover 捕获错误。
异常的使用场景简单描述:Go中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理。
panic:
1、内置函数
2、假如函数F中书写了panic语句,会终止其后要执行的代码,在panic所在函数F内如果存在要执行的defer函数列表,按照defer的逆序执行
3、返回函数F的调用者G,在G中,调用函数F语句之后的代码不会执行,假如函数G中存在要执行的defer函数列表,按照defer的逆序执行
4、直到goroutine整个退出,并报告错误
recover:
1、内置函数
2、用来控制一个goroutine的panicking行为,捕获panic,从而影响应用的行为
3、一般的调用建议
a). 在defer函数中,通过recever来终止一个goroutine的panicking过程,从而恢复正常代码的执行
b). 可以获取通过panic传递的error
注意:
- 1.利用recover处理panic指令,defer 必须放在 panic 之前定义,另外 recover 只有在 defer 调用的函数中才有效。否则当panic时,recover无法捕获到panic,无法防止panic扩散。
- 2.recover 处理异常后,逻辑并不会恢复到 panic 那个点去,函数跑到 defer 之后的那个点。
- 3.多个 defer 会形成 defer 栈,后定义的 defer 语句会被最先调用。
package main
func main() {
test()
}
func test() {
defer func() {
if err := recover(); err != nil {
println(err.(string)) // 将 interface{} 转型为具体类型。
}
}()
panic("panic error!")
}
输出结果:
panic error!
由于 panic、recover 参数类型为 interface{},因此可抛出任何类型对象。
func panic(v interface{})
func recover() interface{}
向已关闭的通道发送数据会引发panic
package main
import (
"fmt"
)
func main() {
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
var ch chan int = make(chan int, 10)
close(ch)
ch <- 1
}
输出结果:
send on closed channel
延迟调用中引发的错误,可被后续延迟调用捕获,但仅最后一个错误可被捕获。
package main
import "fmt"
func test() {
defer func() {
fmt.Println(recover())
}()
defer func() {
panic("defer panic")
}()
panic("test panic")
}
func main() {
test()
}
输出:
defer panic
捕获函数 recover 只有在延迟调用内直接调用才会终止错误,否则总是返回 nil。任何未捕获的错误都会沿调用堆栈向外传递。
package main
import "fmt"
func test() {
defer func() {
fmt.Println(recover()) //有效
}()
defer recover() //无效!
defer fmt.Println(recover()) //无效!
defer func() {
func() {
println("defer inner")
recover() //无效!
}()
}()
panic("test panic")
}
func main() {
test()
}
输出:
defer inner
<nil>
test panic
使用延迟匿名函数或下面这样都是有效的。
package main
import (
"fmt"
)
func except() {
fmt.Println(recover())
}
func test() {
defer except()
panic("test panic")
}
func main() {
test()
}
输出结果:
test panic
如果需要保护代码段,可将代码块重构成匿名函数,如此可确保后续代码被执 。
package main
import "fmt"
func test(x, y int) {
var z int
func() {
defer func() {
if recover() != nil {
z = 0
}
}()
panic("test panic")
z = x / y
return
}()
fmt.Printf("x / y = %d\n", z)
}
func main() {
test(2, 1)
}
输出结果:
x / y = 0
除用 panic 引发中断性错误外,还可返回 error 类型错误对象来表示函数调用状态。
type error interface {
Error() string
}
标准库 errors.New 和 fmt.Errorf 函数用于创建实现 error 接口的错误对象。通过判断错误对象实例来确定具体错误类型。
package main
import (
"errors"
"fmt"
)
var ErrDivByZero = errors.New("division by zero")
func div(x, y int) (int, error) {
if y == 0 {
return 0, ErrDivByZero
}
return x / y, nil
}
func main() {
defer func() {
fmt.Println(recover())
}()
switch z, err := div(10, 0); err {
case nil:
println(z)
case ErrDivByZero:
panic(err)
}
}
输出结果:
division by zero
Go实现类似 try catch 的异常处理
package main
import "fmt"
func Try(fun func(), handler func(interface{})) {
defer func() {
if err := recover(); err != nil {
handler(err)
}
}()
fun()
}
func main() {
Try(func() {
panic("test panic")
}, func(err interface{}) {
fmt.Println(err)
})
}
输出结果:
test panic
如何区别使用 panic 和 error 两种方式?
惯例是:导致关键流程出现不可修复性错误的使用 panic,其他使用 error。
参考资料
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)