【Go专家编程】defer这里有个坑

前言

项目中,有时为了让程序更健壮,也即不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再也没法接收异常。函数

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

recover 失效的条件

上面代码IsPanic()失效了,其缘由是违反了recover的一个限制,致使recover()失效(永远返回nil)。it

如下三个条件会让recover()返回nil:

  1. panic时指定的参数为nil;(通常panic语句如panic("xxx failed...")
  2. 当前协程没有发生panic;
  3. recover没有被defer方法直接调用;

前两条都比较容易理解,上述例子正是匹配第3个条件。

本例中,recover() 调用栈为“defer (匿名)函数” --> IsPanic() --> recover()。也就是说,recover并无被defer方法直接调用。符合第3个条件,因此recover() 永远返回nil。

赠人玫瑰手留余香,若是以为不错请给个赞~

本篇文章已归档到GitHub项目,求星~ 点我即达

相关文章
相关标签/搜索