【GO语言培训教程】小白必看,Go语言异常处理!
老男孩IT教育
技术博客
2021年8月30日 16:16
Go语言没有结构化异常,使用 panic 抛出错误,recover 捕获错误。异常的使用场景简单描述:Go中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理。
Go语言没有结构化异常,使用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语句会被最先调用。
异常处理
一、panic、recover参数类型为interface{},因此可抛出任何类型对象。
func panic(v interface{})
func recover() interface{}
package main
import (
"fmt"
)
func main() {
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
panic("panic error!")
}
二、延迟调用中引发的错误,可被后续延迟调用捕获,但仅最后一个错误可被捕获。
package main
import "fmt"
func main() {
defer func() {
fmt.Println(recover())
}()
defer func() {
panic("defer panic")
}()
panic("panic error")
}
运行结果:
defer panic
三、捕获函数recover只有在延迟调用内直接调用才会终止错误,否则总是返回nil。任何未捕获的错误都会沿调用堆栈向外传递。
package main
import "fmt"
func main() {
defer func() {
fmt.Println(recover()) //有效
}()
defer recover() //无效!
defer fmt.Println(recover()) //无效!
defer func() {
func() {
fmt.Println("defer inner")
recover() //无效!
}()
}()
panic("panic error")
}
运行结果:
defer inner
<nil>
panic error
四、使用延迟匿名函数也有效的。
package main
import (
"fmt"
)
func main() {
defer func() { fmt.Println(recover()) }()
panic("panic error")
}
运行结果:
panic error
五、如果需要保护代码段,可将代码块重构成匿名函数,如此可确保后续代码被执。
package main
import "fmt"
func main() {
divide(2, 1)
}
func divide(x, y int) {
var z int
func() {
defer func() {
if recover() != nil {
z = 0
}
}()
panic("panic error")
z = x / y
return
}()
fmt.Printf("x / y = %d\n", z)
}
运行结果:
x / y = 0
六、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("panic error")
}, func(err interface{}) {
fmt.Println(err)
})
}
输出结果:
panic error
七、goroutine 中使用 recover
如果某个goroutine panic了,而且这个goroutine里面没有捕获(recover),那么整个进程就会挂掉。所以应当产生一个goroutine,就需要写下recover。
package main
import (
"fmt"
"sync"
)
func main() {
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
defer func() {
if err := recover(); err != nil {
fmt.Println("panic:", err)
}
}()
var m map[string]string
m["name"] = "oldboy"
}()
wg.Wait()
}
如何区别使用panic和error两种方式?
惯例是:导致关键流程出现不可修复性错误的使用panic,其他使用error。
老男孩GO语言课程率行业之先开设,并进行多次迭代升级,以帮助学员学习到真正有用的知识,如有学习需求,可以关注“GO语言开发”课程。
推荐阅读:
