defer 的作用和执行时机

go 的 defer 语句是用来延迟执行函数的,而且延迟发生在调用函数 return 之后,比如

func a() int {defer b()return 0
}

b 的执行是发生在 return 0 之后,注意 defer 的语法,关键字 defer 之后是函数的调用。

defer 的重要用途一:清理释放资源

由于 defer 的延迟特性,defer 常用在函数调用结束之后清理相关的资源,比如

f, _ := os.Open(filename)
defer f.Close()

文件资源的释放会在函数调用结束之后借助 defer 自动执行,不需要时刻记住哪里的资源需要释放,打开和释放必须相对应。

用一个例子深刻诠释一下 defer 带来的便利和简洁。

代码的主要目的是打开一个文件,然后复制内容到另一个新的文件中,没有 defer 时这样写:

func CopyFile(dstName, srcName string) (written int64, err error) {src, err := os.Open(srcName)if err != nil {return}dst, err := os.Create(dstName)if err != nil { //1return}written, err = io.Copy(dst, src)dst.Close()src.Close()return
}

代码在 #1 处返回之后,src 文件没有执行关闭操作,可能会导致资源不能正确释放,改用 defer 实现:

func CopyFile(dstName, srcName string) (written int64, err error) {src, err := os.Open(srcName)if err != nil {return}defer src.Close()dst, err := os.Create(dstName)if err != nil {return}defer dst.Close()return io.Copy(dst, src)
}

src 和 dst 都能及时清理和释放,无论 return 在什么地方执行。

鉴于 defer 的这种作用,defer 常用来释放数据库连接,文件打开句柄等释放资源的操作。

defer 的重要用途二:执行 recover

被 defer 的函数在 return 之后执行,这个时机点正好可以捕获函数抛出的 panic,因而 defer 的另一个重要用途就是执行 recover。

recover 只有在 defer 中使用才更有意义,如果在其他地方使用,由于 program 已经调用结束而提前返回而无法有效捕捉错误。

package mainimport ("fmt"
)func main() {defer func() {if ok := recover(); ok != nil {fmt.Println("recover")}}()panic("error")}

记住 defer 要放在 panic 执行之前。

多个 defer 的执行顺序

defer 的作用就是把关键字之后的函数执行压入一个栈中延迟执行,多个 defer 的执行顺序是后进先出 LIFO :

defer func() { fmt.Println("1") }()
defer func() { fmt.Println("2") }()
defer func() { fmt.Println("3") }()

输出顺序是 321。

这个特性可以对一个 array 实现逆序操作。

被 deferred 函数的参数在 defer 时确定

这是 defer 的特点,一个函数被 defer 时,它的参数在 defer 时进行计算确定,即使 defer 之后参数发生修改,对已经 defer 的函数没有影响,什么意思?看例子:

func a() {i := 0defer fmt.Println(i)i++return
}

a 执行输出的是 0 而不是 1,因为 defer 时,i 的值是 0,此时被 defer 的函数参数已经进行执行计算并确定了。

再看一个例子:

func calc(index string, a, b int) int {ret := a + bfmt.Println(index, a, b, ret)return ret
}func main() {a := 1b := 2defer calc("1", a, calc("10", a, b))a = 0return
}

执行代码输出

10 1 2 3
1 1 3 4

defer 函数的参数 第三个参数在 defer 时就已经计算完成并确定,第二个参数 a 也是如此,无论之后 a 变量是否修改都不影响。

被 defer 的函数可以读取和修改带名称的返回值

func c() (i int) {defer func() { i++ }()return 1
}

被 defer 的函数是在 return 之后执行,可以修改带名称的返回值,上面的函数 c 返回的是 2。

原文链接:https://sanyuesha.com/2017/07/23/go-defer/

理解 Go defer相关推荐

  1. 深入理解golang的defer

    defer 估计是每个 Gopher 每天写代码都会写,那么你是不是真正的理解了 defer 呢?不妨看一下下面这个代码片段,这个是我之前给 UC 那边一个 team 做 Golang 培训的时候想的 ...

  2. Golang之轻松化解defer的温柔陷阱

    什么是defer? defer是Go语言提供的一种用于注册延迟调用的机制:让函数或语句可以在当前函数执行完毕后(包括通过return正常结束或者panic导致的异常结束)执行. defer语句通常用于 ...

  3. Go进阶(3): 函数+闭包+defer

    1. 函数是一种数据类型 函数参数的传递方式有值传递和引用传递(指针/slicer/map/channel/interface)两种:不管哪一种,传递的都是值或者地址的副本:一般来说,地址传递的效率更 ...

  4. Golang 之轻松化解 defer 的温柔陷阱

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

  5. html5中defer的属性,HTML5中defer和async的比较

    在网站页面中,通常需要引入外部js资源,然而外部的js资源可能导致DOM阻塞,影响页面加载速度.通过异步或者延迟执行js,可以做到引用外部js资源而不阻塞DOM的目的.用法是直接在script标签中使 ...

  6. 理解fmt||net/http in Go

    fmt Package fmt implements formatted I/O with functions analogous to C's printf and scanf. The forma ...

  7. Golang之轻松化解defer的温柔陷阱 1

    defer是Go语言提供的一种用于注册延迟调用的机制:让函数或语句可以在当前函数执行完毕后(包括通过return正常结束或者panic导致的异常结束)执行.深受Go开发者的欢迎,但一不小心就会掉进它的 ...

  8. 浏览器是如何解析html的?

    当我们在浏览器地址栏输入一个合法的url时,浏览器首先进行DNS域名解析,拿到服务器IP地址后,浏览器给服务器发送GET请求,等到服务器正常返回后浏览器开始下载并解析html.这里仅总结浏览器解析ht ...

  9. Caddy、 SSLDocker、Nginx 性能比较及使用体验

    Caddy. SSLDocker.Nginx 性能比较及使用体验 Caddy. SSLDocker.Nginx 都是可以用来做前端代理的服务,前两者是用go来写,部署比较简单. Nginx 在部署HT ...

最新文章

  1. .net框架读书笔记---通用对象操作(一)
  2. sqlserver 触发器 update_运维日记| SQL server 那点事——DML触发器
  3. post请求改成body_post请求body格式
  4. Execution default of goal org.springframework.boot:spring-boot-maven-plugin
  5. 中国酒精炉行业市场供需与战略研究报告
  6. Nginx负载均衡如何进行配置
  7. 在.net 应用MD5加密
  8. 我国农业谋定国际竞争战略取向 对话国际农民丰收节贸易会
  9. linux阅读文件格式,Linux下安装boox viewer阅读pdg格式文件
  10. JS 大陆香港台湾手机格式校验
  11. Android 语音助手
  12. mysql数据库repair_MySQL数据库中的REPAIRTABLE语法介绍
  13. 计算机cad运行缓慢怎样处理,旧电脑如何提高CAD运行速度
  14. JS数据结构与算法-队列结构
  15. Mysql 解决 Your password has expired.
  16. 微信小程序获取手机号登录流程
  17. 节拍脉冲发生器的设计
  18. 被骗的还不够吗?物联网卡这个陷阱千万要注意!
  19. 别光顾着吃瓜,今天来讲讲微博为何总宕机
  20. 淘宝订单API获取订单代码说明

热门文章

  1. 汇编和python-python和汇编语言的区别知识点
  2. python培训比较好的机构-学Python去哪家培训机构比较好?
  3. python处理excel字典-使用Python代码处理Excel
  4. python类型-python基础之五大标准数据类型
  5. python三层装饰器-python中自带的三个装饰器的实现
  6. python syntaxerror怎么解决-python中出现invalid syntax报错的几种原因
  7. 在哪里能收到python实例代码-python实例代码
  8. python流程控制-python之流程控制
  9. 学python买什么电脑-程序员,买了台破Apple电脑,用来学Python
  10. python语言怎么学-你们都是怎么学 Python 的?