博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
深入理解 Defer 函数
阅读量:2289 次
发布时间:2019-05-09

本文共 3533 字,大约阅读时间需要 11 分钟。

点击上方蓝色“Golang来啦”关注我哟

加个“星标”,天天 15 分钟,掌握 Go 语言

via:

https://medium.com/technofunnel/understanding-golangs-defer-function-cd262e8cf54c
作者:Mayank Gupta
四哥水平有限,如有翻译或理解错误,烦请帮忙指出,感谢!

这篇文章源自 Medium,文章点赞 200+。

原文如下:


Golang 提供了函数式编程模型,为函数式编程增加了更强大的功能和灵活性,引进了其他服务端编程语言没有的新特性。其中一个新特性就是 Defer。

这篇文章会与大家探讨 Defer 关键字的一些细节,并且会分析该函数是如何给程序带来更强大的灵活性。

defer 的特性:

  1. 函数返回时会调用 defer 定义的函数;

  2. 无论函数的执行情况如何,defer 定义的函数总是会被执行;

  3. defer 可以在函数内部的任何地方使用;

  4. 可以定义多次 defer 函数调用;

  5. 可以用来释放资源;

代码里释放资源

让我们假设一种需要释放文件资源的场景。我们需要打开并读取文件里的内容,对文件内容会做一些操作。打开文件并读取内容之后,需要关闭文件句柄。

我们可能具有执行多个任务的复杂功能,每个函数都有多个 “return” 关键字。然而函数返回时,可能需要执行一些清理操作。

清理操作可能包括:

  1. 关闭文件;

  2. 关闭数据库;

  3. 取消订阅某些事件;

  4. 函数执行报错情况下资源清理;

身为开发人员,我们需要确保函数返回时完成清理工作。某些意外情况下,程序可能没有按照正常情况返回。我们需要一种在任何情况下,都能完成清理工作的机制。

在处理非常负责的实时程序时,开发人员有可能会漏掉某些需要执行清理操作的地方和场景。

我们通过一个例子来理解下这个问题:

package mainimport (    "fmt"    "os")func main() {    GetFileContent()}func GetFileContent() string {    file, _ := os.Open("./FileContent/sampleFile.txt")    data := make([]byte, 100)    file.Read(data)    dataString := string(data)    fmt.Printf(dataString)  if(dataString == "Hello Mayank") {    file.Close()    return "Mayank";  } else {    file.Close()    return "Anshul"  }}

上面的例子,我们试图访问文件的内容,一旦完成文件内容的读取就需要关闭文件。假设基于文件内容会有不同的操作,函数里使用了多个 return。需要确保函数返回时,文件已经关闭。所以我们需要在函数多个地方调用 file.Close()。

代码如下:

func GetFileContent() string {    file, _ := os.Open("./FileContent/sampleFile.txt")    data := make([]byte, 100)    file.Read(data)    dataString := string(data)    fmt.Printf(dataString)  if(dataString == "Mayank") {    file.Close()    return "Mayank";  } else {    file.Close()    return "Anshul"  }}

使用 defer 关键字

针对上述场景,上面的解决办法使得程序变得更复杂了,我们来看下 defer 关键字如何解决这个问题。在任何函数之前加上 defer 关键字后,该函数将会在 return 时被执行。defer 的使用方法很简单,如下代码:

func InvokeDeferFunction() {  defer DeferFunctionCall()  fmt.Println("Defer Function Still Not Called...")}func DeferFunctionCall() {  fmt.Println("Defer Function is called after function call...")}

上面的代码需要注意以下几点:

  1. defer 关键字需要添加在 DeferFunctionCall() 函数之前(左边);

  2. defer 定义的函数添加在执行函数的最顶部;

上面的代码输出如下:

从输出结果我们会发现,尽管 defer 定义的函数放在了执行函数的最顶部,但是当程序执行到 defer 那行代码时,defer 函数仍没有执行,该函数是执行函数返回时被执行到的。在退出 InvokeDeferFunction() 函数之前,defer 定义的函数会被执行。我们可以利用这个特性,在函数返回之前执行多个 defer 函数。

添加多个 defer 函数

在当前执行函数返回之前,我们可以执行多个 defer 函数。多个 defer 函数完成一些特殊的功能。我们试着使用多个 defer 函数扩展上面的代码。

package mainimport "fmt"func main() {  InvokeDeferFunction()}func InvokeDeferFunction() {  defer DeferFunctionCall()  defer OtherDeferFunctionCall()  fmt.Println("Still executing InvokeDeferFunction")}func DeferFunctionCall() {  fmt.Println("Defer Function Called...")}func OtherDeferFunctionCall() {  fmt.Println("Other Defer Function called...")}

上面的代码,我们可以看到定义了多个 defer 函数。这些函数会被压入栈中,将会按照 LIFO(后入先出) 顺序执行。上面的代码输出如下:

通过上面的代码,我们会发现:

  1. 多个 defer 函数会被调用;

  2. defer 函数按照后入先出的顺序执行;

处理文件资源

我们回过头来看下最开始读取文件的例子,我们使用 defer 关键字重写代码。我们接着往下看,使用 defer 之后代码会是什么样子的。

func GetFileContent() string {    file, _ := os.Open("./FileContent/sampleFile.txt")  defer file.Close()    data := make([]byte, 100)    file.Read(data)    dataString := string(data)    fmt.Printf(dataString)  if(dataString == "Hello Mayank") {    return "Mayank";  } else {    return "Anshul"  }}

上面的代码中,使用 defer 调用了关闭文件的函数,该函数会在执行函数返回之前被调用。无论函数执行过程中发生什么意外,文件关闭函数都会被执行。

这种处理方式可以提供以下便利:

  1. 分配资源的代码与销毁或关闭资源的代码可以放在一块,例如在打开文件或创建数据库连接时,紧接着可以在下一行代码定义关闭资源的函数,这样的话,即使函数再复杂,开发人员也不会忘记这些清理工作;

  2. 每一个 defer 定义的函数完成不同的特殊功能,例如,一个 defer  函数处理文件资源,另一个 defer 函数关闭网络连接;

使用 defer 处理 Error

这可能是 defer 关键字最重要的功能,可以帮助开发人员处理 errors。可以使用下面几个关键字处理 error。

  1. Panic

  2. Defer

  3. Recover

关于如何使用 defer 关键字处理错误的细节介绍,会在下一篇文章给大家安排。

推荐阅读:

如果我的文章对你有所帮助,点赞、转发都是一种支持!

给个[在看],是对四哥最大的支持

转载地址:http://yyfnb.baihongyu.com/

你可能感兴趣的文章
MySQL group by的sql_mode报错
查看>>
联合索引应用细节
查看>>
Nginx地址重定向 return rewrite if
查看>>
PHP安装Redis模块
查看>>
PHP追加安装时候忘装的模块
查看>>
PHP平滑升级
查看>>
MySQL删除无用账户
查看>>
MySQL多实例配置方案
查看>>
MySQL设置及修改root密码
查看>>
MySQL忘记密码重置管理员密码
查看>>
MySQL创建及授权账号
查看>>
MySQL库的基本操作
查看>>
MySQL表的基本操作
查看>>
MySQL数据类型
查看>>
MySQL SQL语句最常见的分类
查看>>
MySQL用户权限
查看>>
MySQL数据备份
查看>>
MySQL使用explain检查索引执行计划
查看>>
MySQL字符集
查看>>
MySQL存储引擎
查看>>