项目中,有时为了让程序更健壮,也即不panic
,咱们或许会使用recover()
来接收异常并处理。git
好比如下代码:github
func NoPanic() { if err := recover(); err != nil { fmt.Println("Recover success...") } } func Dived(n int) { defer NoPanic() fmt.Println(1/n) }
func NoPanic()
会自动接收异常,并打印相关日志,算是一个通用的异常处理函数。golang
业务处理函数中只要使用了defer NoPanic()
,那么就不会再有panic
发生。数据库
关因而否应该使用recover接收异常,以及什么场景下使用等问题不在本节讨论范围内。 本节关注的是这种用法的一个变体,曾经出如今笔者经历的一个真实项目,在该变体下,recover再也没法接收异常。函数
在项目中,有众多的数据库更新操做,正常的更新操做须要提交,而失败的就须要回滚,若是异常分支比较多, 就会有不少重复的回滚代码,因此有人尝试了一个作法:即在defer中判断是否出现异常,有异常则回滚,不然提交。日志
简化代码以下所示:code
func IsPanic() bool { if err := recover(); err != nil { fmt.Println("Recover success...") return true } return false } func UpdateTable() { // defer中决定提交仍是回滚 defer func() { if IsPanic() { // Rollback transaction } else { // Commit transaction } }() // Database update operation... }
func IsPanic() bool
用来接收异常,返回值用来讲明是否发生了异常。func UpdateTable()
函数中,使用defer来判断最终应该提交仍是回滚。协程
上面代码初步看起来还算合理,可是此处的IsPanic()
不再会返回true
,不是IsPanic()
函数的问题,而是其调用的位置不对。get
上面代码IsPanic()
失效了,其缘由是违反了recover的一个限制,致使recover()失效(永远返回nil
)。it
如下三个条件会让recover()返回nil
:
nil
;(通常panic语句如panic("xxx failed...")
)前两条都比较容易理解,上述例子正是匹配第3个条件。
本例中,recover() 调用栈为“defer (匿名)函数” --> IsPanic() --> recover()。也就是说,recover并无被defer方法直接调用。符合第3个条件,因此recover() 永远返回nil。
赠人玫瑰手留余香,若是以为不错请给个赞~
本篇文章已归档到GitHub项目,求星~ 点我即达