分享
  1. 首页
  2. 文章

defer ,panic,recover

普朗tong · · 1100 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

defer的执行顺序

多个defer出现的时候,它是一个"栈"的关系,也就是先进后出。一个函数中,写在前面的defer会比写在后面的defer调用的晚。

package main
import "fmt"
func main() {
 defer func1()
 defer func2()
 defer func3()
}
func func1() {
 fmt.Println("A")
}
func func2() {
 fmt.Println("B")
}
func func3() {
 fmt.Println("C")
}
//输出结果:C B A

defer和return的先后顺序

return之后的语句先执行,defer后的语句后执行。

package main
import "fmt"
func deferFunc() int {
 fmt.Println("defer func called")
 return 0
}
func returnFunc() int {
 fmt.Println("return func called")
 return 0
}
func returnAndDefer() int {
 defer deferFunc()
 return returnFunc()
}
func main() {
 returnAndDefer()
}
//输出
//return func called
//defer func called

defer 无名返回值 有名返回值

  • 函数返回值初始化
    该知识点不属于defer本身,但是调用的场景却与defer有联系,所以也算是defer必备了解的知识点之一。
    如 : func DeferFunc1(i int) (t int) {}
    其中返回值t int,这个t会在函数起始处被初始化为对应类型的零值并且作用域为整个函数。
package main
import "fmt"
func DeferFunc(i int) (t int) {
 fmt.Println("t = ", t)
 return 2
}
func main() {
 DeferFunc(10)
}
//输出
//t = 0
  • defer、return 和无名的返回值
package main
func main() {
 name := run()
 println("return: name = " + name) 
}
func run() (string) {
 name := "Paul"
 defer sayHello(&name)
 name = "John"
 return name
}
func sayHello(name *string) {
 *name = "George"
 println("Hello " + *name)
}
// 输出
// Hello George
// return: name = John
  • defer、return 和有名返回值
//传指针
package main
func main() {
 name := run()
 println("return: name = " + name)
}
func run() (x string) {
 name := "Paul"
 x = name
 defer sayHello(&x)
 name = "John"
 return name
}
func sayHello(name *string) {
 println("Hello " + *name)
 *name = "George"
 println("Hello " + *name)
}
//输出
//Hello John
//Hello George
//return: name = George
//传值
package main
func main() {
 name := run()
 println("return: name = " + name)
}
func run() (x string) {
 name := "Paul"
 x = name
 defer sayHello(x)
 name = "John"
 return name
}
func sayHello(name string) {
 println("Hello " + name)
 name = "George"
 println("Hello " + name)
}
//输出
//Hello Paul 
//Hello George 
//return: name = John

说明:defer,return 和返回值三者之间的执行逻辑
return最先执行,return负责将结果写入返回值中;接着defer开始执行一些收尾工作;最后函数携带当前返回值退出

defer 闭包

  • 闭包是什么
    闭包是由函数及其相关引用环境组合而成的实体,即
    闭包=函数+引用环境

一般的函数都有函数名,但是匿名函数就没有。匿名函数不能独立存在,但可以直接调用或者赋值于某个变量。匿名函数也被称为闭包,一个闭包继承了函数声明时的作用域。在Golang中,所有的匿名函数都是闭包。

有个不太恰当的例子,可以把闭包看成是一个类,一个闭包函数调用就是实例化一个类。闭包在运行时可以有多个实例,它会将同一个作用域里的变量和常量捕获下来,无论闭包在什么地方被调用(实例化)时,都可以使用这些变量和常量。而且,闭包捕获的变量和常量是引用传递,不是值传递。

package main
func main() {
 name := run()
 println("return: name = " + name)
}
func run() (string) {
 name := "Paul"
 aFun := func() {
 println("Hello " + name)
 name = "George"
 println("Hello " + name)
 }
 name = "John"
 aFun()
 bfunc:= func() {
 println("Hello " + name)
 name = "Bobbi"
 println("Hello " + name)
 }
 bfunc()
 return name
}
//输出
//Hello John
//Hello George
//Hello George
//Hello Bobbi
//return: name = Bobbi
  • defer与闭包
package main
func main() {
 name := run()
 println("return: name = " + name)
}
func run() (x string) {
 name := "Paul"
 aFun := func() {
 println("Hello " + x)
 x = "George"
 println("Hello " + x)
 }
 defer aFun()
 name = "John"
 return name
}
//输出
//Hello John
//Hello George
//return: name = George
package main
func main() {
 name := run()
 println("return: name = " + name)
}
func run() (x string) {
 name := "Paul"
 x = name
 aFun := func(x string) {
 println("Hello " + x)
 x = "George"
 println("Hello " + x)
 }
 defer aFun(x)
 name = "John"
 return name
}
//输出:
//Hello Paul
//Hello George
//return: name = John

defer panic recover

能够触发defer的是遇见return(或函数体到末尾)和遇见panic。
遇见panic的情况:遍历本协程的defer链表,并执行defer。在执行defer过程中:遇到recover则停止panic,返回recover处继续往下执行。如果没有遇到recover,遍历完本协程的defer链表后,向stderr抛出panic信息。
A. defer遇见panic,但是并不捕获异常的情况

package main
import (
 "os"
 "fmt"
 "time"
)
func main() {
 var user = os.Getenv("USER_")
 go func() {
 defer func() {
 fmt.Println("defer here")
 }()
 if user == "" {
 panic("should set user env.")
 }
 }()
 time.Sleep(1 * time.Second)
 fmt.Printf("get result %d\r\n", 1)
}
//输出
//defer here
//panic: should set user env. 

说明:会发现defer中的字符串"defer here"打印出来了,而main流程中的"ger result"没有打印,说明panic坚守了自己的原则:
执行,且只执行,当前goroutine的defer

B.defer遇见panic,并捕获异常

package main
import (
 "fmt"
)
func main() {
 defer_call()
 fmt.Println("main 正常结束")
}
func defer_call() {
 defer func() {
 fmt.Println("defer: panic 之前1, 捕获异常")
 if err := recover(); err != nil {
 fmt.Println(err)
 }
 }()
 defer func() { fmt.Println("defer: panic 之前2, 不捕获") }()
 panic("异常内容") //触发defer出栈
 defer func() { fmt.Println("defer: panic 之后, 永远执行不到") }()
}
//输出
//defer: panic 之前2, 不捕获
//defer: panic 之前1, 捕获异常
//异常内容
//main 正常结束

参考:
Golang中的Defer必掌握的7知识点
Golang中defer、闭包以及命名返回值
Golang: 深入理解panic and recover


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:普朗tong

查看原文:defer ,panic,recover

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
1100 次点击
1 回复 | 直到 2025年05月12日 00:00:13
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏