文档章节

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

恋恋美食
 恋恋美食
发布于 09/20 21:42
字数 674
阅读 28
收藏 0
Go

前言

项目中,有时为了让程序更健壮,也即不panic,我们或许会使用recover()来接收异常并处理。

比如以下代码:

func NoPanic() {
	if err := recover(); err != nil {
		fmt.Println("Recover success...")
	}
}

func Dived(n int) {
	defer NoPanic()

	fmt.Println(1/n)
}

func NoPanic() 会自动接收异常,并打印相关日志,算是一个通用的异常处理函数。

业务处理函数中只要使用了defer NoPanic(),那么就不会再有panic发生。

关于是否应该使用recover接收异常,以及什么场景下使用等问题不在本节讨论范围内。 本节关注的是这种用法的一个变体,曾经出现在笔者经历的一个真实项目,在该变体下,recover再也无法接收异常。

recover使用误区

在项目中,有众多的数据库更新操作,正常的更新操作需要提交,而失败的就需要回滚,如果异常分支比较多, 就会有很多重复的回滚代码,所以有人尝试了一个做法:即在defer中判断是否出现异常,有异常则回滚,否则提交。

简化代码如下所示:

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()函数的问题,而是其调用的位置不对。

recover 失效的条件

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

以下三个条件会让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项目,求星~ 点我即达

© 著作权归作者所有

恋恋美食

恋恋美食

粉丝 113
博文 159
码字总数 147008
作品 0
杭州
高级程序员
私信 提问
script的加载方式与执行

script一般是阻塞式加载的,H5新增了、和特性,可用于异步加载/延迟执行: async 属性是指当这个 script 可用时,就异步执行它 defer 属性是指当页面被解析完毕后,才能执行 如果以上两个属性...

一点灵犀
2016/06/16
42
0
Golang中多用途的defer

defer顾名思义就是延迟执行,那么defer在Golang中该如何使用以及何时使用呢? A "defer" statement invokes a function whose executionis deferred to the moment the surrounding function......

gotaly
2018/06/26
0
0
Golang defer 使用时的坑

defer是golang语言中的关键字,用于资源的释放,会在函数返回之前进行调用。一般采用如下模式: f,err := os.Open(filename)if err != nil { }defer f.Close() 如果有多个defer表达式,调用顺...

徐学良
2016/07/20
212
0
defer函数参数求值简要分析

defer函数参数求值简要分析 Tony Bai2018-03-231 阅读 FunctiondeferGo 一. 引子 书接上文,在发表了 《对一段Go语言代码输出结果的简要分析》 一文之后,原问题提出者又有了新问题,这是一个...

Tony Bai
2018/03/23
0
0
Golang 之轻松化解 defer 的温柔陷阱

作者 | 饶全成 责编 | 胡巍巍 defer是Go语言提供的一种用于注册延迟调用的机制:让函数或语句可以在当前函数执行完毕后(包括通过return正常结束或者panic导致的异常结束)执行。 深受Go开发...

CSDN资讯
03/19
0
0

没有更多内容

加载失败,请刷新页面

加载更多

浅谈Visitor访问者模式

一、前言 什么叫访问,如果大家学过数据结构,对于这点就很清晰了,遍历就是访问的一般形式,单独读取一个元素进行相应的处理也叫作访问,读取到想要查看的内容+对其进行处理就叫作访问,那么...

青衣霓裳
29分钟前
6
0
JS内嵌多个页面,页面之间如何更快捷的查找相关联的页面

假设parent为P页面, P页面有两个子页面,分别为B页面和C页面; B页面和C页面分别内嵌一个iframe,分别为:D页面和E页面 现在通过B页面的内嵌页面D的方法refreshEpage(eUrl)来加载内嵌页面E的内容...

文文1
30分钟前
7
0
Hibernate 5 升级后 getProperties 错误

升级到 Hibernate 5 后,提示有错误: org.hibernate.engine.spi.SessionFactoryImplementor.getProperties()Ljava/util/Map; 完整的错误栈为: java.lang.NoSuchMethodError: org.hibernate......

honeymoose
31分钟前
6
0
mysql-connector-java升级到8.0后保存时间到数据库出现了时差

在一个新项目中用到了新版的mysql jdbc 驱动 <dependency>     <groupId>mysql</groupId>     <artifactId>mysql-connector-java</artifactId>     <version>8.0.18</version> ......

ValSong
34分钟前
7
0
Spring中BeanFactory与FactoryBean的区别

在Spring中有BeanFactory和FactoryBean这2个接口,从名字来看很相似,比较容易搞混。 一、BeanFactory BeanFactory是一个接口,它是Spring中工厂的顶层规范,是SpringIoc容器的核心接口,它定...

大王叫下
37分钟前
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部