Go语言函数的延迟调用(DeferredCode)详解
本篇文章向大家介绍《Go语言函数的延迟调用(DeferredCode)详解》,主要包括函数、延迟、调用,具有一定的参考价值,需要的朋友可以参考一下。
先解释一下这篇Blog延期的原因,本来已经准备好了全部内容,但是当我重新回顾实例三的时候,发现自己还是存在认知不足的地方,于是为了准确表述,查阅了大量的资料,重新编写了第三部分,导致延期。感谢持续关注本笔记更新的朋友,后期我将逐步通过3-5分钟视频方式为大家对笔记内容进行讲解,帮助更多的朋友能够快速掌握Go语言的基础。
本节将介绍Go语言函数和方法中的延迟调用,正如名称一样,这部分定义不会立即执行,一般会在函数返回前再被调用,我们通过下面的几个示例来了解一下延迟调用的使用场景。
基本功能
在以下这段代码中,我们操作一个文件,无论成功与否都需要关闭文件句柄。这里在三处不同的位置都调用了file.Close()方法,代码显得非常冗余。
func ReadWrite() bool {
file.Open("file")
// Do your thing
if failureX {
file.Close()
return false
}
if failureY {
file.Close()
return false
}
file.Close()
return true
}
我们利用延迟调用来优化代码。定义后的defer代码,会在return之前返回,让代码显得更加紧凑,且可读性变强,对上面的代码改造如下:
func ReadWrite() bool {
file.Open("filename")
// Define a defer code here
defer file.Close()
// Do your thing
if failureX {
return false
}
if failureY {
return false
}
return true
}
示例一:延迟调用执行顺序
我们通过这个示例来看一下延迟调用与正常代码之间的执行顺序
package main
import "fmt"
func TestDefer(x int) {
defer fmt.Println("Defer code called")
switch x {
case 1:
fmt.Println("Case 1 triggered!")
return
case 10:
fmt.Println("Case 10 triggered!")
return
default:
fmt.Println("Case default triggered!")
return
}
}
func main() {
TestDefer(100)
TestDefer(1)
TestDefer(10)
}
先简单分析一下代码逻辑:
- 首先定义了一个公共的TestDefer函数,这个函数接受一个整型的参数
- 函数体内定义了defer部分,会输出一句Defer code called
- switch case会根据输入的整型参数,输出相应的trigger语句
- 按照上面对延迟调用的分析,每次满足case语句后,才会输出Defer code called
从输出中,我们可以观察到如下现象:
- 首次执行,default条件满足,Case default triggered先输出,再输出defer内容
- 第二次调用,1条件满足,最后输出defer内容
- 第三次调用,10条件满足,最后输出defer内容
从这个实例中,我们很明显观察到,defer语句是在return之前执行
Case default triggered!
Defer code called
Case 1 triggered!
Defer code called
Case 10 triggered!
Defer code called
示例二:多defer使用方法
package main
import "fmt"
func TestDefer(x int) {
defer fmt.Println("1st defined Defer code called")
defer fmt.Println("2nd defined Defer code called")
defer fmt.Println("3rd defined Defer code called")
switch x {
case 1:
fmt.Println("Case 1 triggered!")
return
case 10:
fmt.Println("Case 10 triggered!")
return
default:
fmt.Println("Case default triggered!")
return
}
}
func main() {
TestDefer(100)
}
仍然是相同的例子,但是在TestDefer中我们定义了三个defer输出,根据LIFO原则,输出的顺序是3rd->2nd->1st,根据最后的结果,也是逆向向上执行defer输出。
Case default triggered!
3rd defined Defer code called
2nd defined Defer code called
1st defined Defer code called
实例三:defer与局部变量、返回值的关系
就在整理这篇笔记的时候,发现了自己的认知误区,主要是本节实例三中发现的,先来看一下英文的描述:
A defer statement pushes a function call onto a list. The list of saved calls is executed after the surrounding function returns. Defer is commonly used to simplify functions that perform various clean-up actions.
对于上面的这段话的理解:
defer定义的函数会被放入list中
存储的defer函数会在周边函数返回后执行
defer一般用于环境清理
原则一:defer函数的参数值,取决于defer函数调用时变量的值
package main
import "fmt"
func a() int {
i := 0
fmt.Printf("func i = %v\n", i)
defer fmt.Printf("defer i = %v\n", i)
i++
fmt.Printf("func i = %v\n", i)
defer fmt.Printf("defer after i++ = %v\n", i)
return i
}
func main() {
i := a()
fmt.Printf("main i = %v\n", i)
}
下面是代码执行输出,我们来一起分析一下:
- 在函数a中,定义了局部变量i
- 在函数执行过程中进行了自增操作i++
- 分别在i++前后,对i值进行了输出,也就是我们下面输出结果前两行,与预期一致
- 分别在i++前后,定义两个defer语句,都是用fmt输出i的值,输出的顺序与示例二的逻辑一致,先输出的是defer after,再输出defer
- 根据原则一,在defer after的输出中,由于i++完成自增,所以当时i的值已经变为了1,所以输出为1
- 同样是根据原则一,在defer的输出中,i并没有进行自增,所以在当时情况下,i的值仍然为0,所以输出为0
- 最后返回的i值为1,主函数中输出i的值为1
func i = 0 func i = 1 defer after i++ = 1 defer i = 0 main i = 1
原则二:defer可以读取或修改显示定义的返回值
package main
import "fmt"
func a() (i int) {
fmt.Printf("func initial i = %v\n", i)
defer func() {
fmt.Printf("defer func initial i++ = %v\n", i)
i++
fmt.Printf("defer func after i++ = %v\n", i)
}()
fmt.Printf("func before return i = %v\n", i)
return 10
}
func main() {
i := a()
fmt.Printf("main i = %v\n", i)
}
虽然在a()函数内,显示的返回了10,但是main函数中得到的结果是defer函数自增后的结果,我们来分析一下代码:
在a函数定义时,我们显示的定义了返回变量i和类型int
在刚刚进入函数时,i的初始化值位0,返回前也是0
在最后的return时,直接返回了10
接着我们再来看defer函数执行情况,刚刚进入defer函数时,返回值i得到的值正是刚才返回的10
而在自增后,i的值变成了11
最后我们在主函数中,获得的返回值也是11,印证了我们原则中的defer函数对于返回值的读取和修改
func initial i = 0 func before return i = 0 defer func initial i++ = 10 defer func after i++ = 11 main i = 11
今天带大家了解了函数、延迟、调用的相关知识,希望对你有所帮助;关于Golang的技术知识我们会一点点深入介绍,欢迎大家关注golang学习网公众号,一起学习编程~
Go语言中的变量和常量
- 上一篇
- Go语言中的变量和常量
- 下一篇
- 一篇文章带你轻松搞懂Golang的error处理
-
- 甜美的美女
- 这篇文章内容出现的刚刚好,太细致了,很好,已收藏,关注师傅了!希望师傅能多写Golang相关的文章。
- 2023-06-09 00:40:45
-
- 粗暴的帆布鞋
- 这篇技术贴真及时,太细致了,很好,mark,关注楼主了!希望楼主能多写Golang相关的文章。
- 2023-02-01 10:51:42
-
- 寂寞的宝马
- 受益颇多,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,帮助很大,总算是懂了,感谢大佬分享技术文章!
- 2023-01-14 19:41:12
-
- Golang · Go教程 | 3天前 | goroutine · Context · 超时控制 · Go教程 · 后端开发 · Go Goroutine context 超时控制 WithTimeout Done QueryContext
- Go context 超时控制实战:从接口入口到 goroutine 回收的完整流程
- 166浏览 收藏
-
- Golang · Go教程 | 5天前 | map · 并发安全 · RWMutex · sync.Map · Go教程 · 并发安全 RWMutex sync.Map Go map并发读写 go test race
- Go map 并发读写崩溃怎么办:从复现报错到 RWMutex 修复的完整流程
- 272浏览 收藏
-
- Golang · Go教程 | 1星期前 | singleflight · 并发控制 · Go教程 · 缓存治理 · 接口优化 · Go 并发请求 缓存击穿 singleflight 缓存回填
- Go singleflight 防缓存击穿实战:相同请求只查一次数据库
- 114浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- ljg-skills
- ljg-skills 是李继刚开源的 AI 技能与提示词集合,面向大模型使用者整理了一批可复用的 prompt、角色设定和任务技能模板,适合用于学习提示词设计、搭建个人 AI 工作流和沉淀团队常用智能体能力。
- 1303次使用
-
- MELO音乐
- MELO音乐是一站式AI视频与音乐制作助手,对标suno, udio的高品质体验。提供伴奏生成、原创写词、无损导出、哼唱识曲、混音变声等全套音频与短视频编辑工具。无论是流行Kpop、电音说唱、民谣古风、摇滚儿歌还是商用轻音乐,MELO为你免费谱曲,轻松做同款!
- 1237次使用
-
- UniScribe
- UniScribe 是一款 AI 音视频转文字与内容整理工具,支持上传音频、视频文件或粘贴 YouTube 链接,自动生成转写文本、摘要、思维导图和关键问题,并支持多格式导出,适合会议记录、课程学习、访谈整理和内容创作复盘。
- 1189次使用
-
- 剧云
- 剧云是专业中文剧本创作平台,安全稳定运行十余年,集成AI编剧、剧本医生审核、人物小传、剧情关系图、大纲编写、多人协作、Word导入导出、版权管控功能,数据安全防护,轻松高效创作剧本。
- 1358次使用
-
- 万象有声
- 万象有声,一个专为有声创作者打造的新一代智能有声内容创作平台。平台提供专业的智能拆章、智能画本编辑、AI配音、AI生成音效、后期制作、智能对轨、智能审听等有声创作全流程工具,可以帮助创作者高效、低成本创作出引人入胜的有声作品。立即体验,让有声书制作更简单!
- 1359次使用
-
- golang中的defer函数理解
- 2022-12-22 366浏览
-
- 基于Golang实现延迟队列(DelayQueue)
- 2023-02-16 321浏览
-
- Go+Redis实现延迟队列实操
- 2023-02-23 426浏览
-
- Golang优雅保持main函数不退出的办法
- 2023-01-07 272浏览
-
- Go语言中函数可变参数(VariadicParameter)详解
- 2023-01-07 312浏览

