Golang 提供了 defer
关键字,用于函数退出前执行收尾工作,基本的使用方法就不再赘述了。总结了一些可能踩坑的地方。
defer 执行顺序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package main
import "fmt"
func TestFunc() { defer func() { fmt.Println("A") }()
defer func() { fmt.Println("B") }()
defer func() { fmt.Println("C") }() }
func main() { TestFunc() }
|
执行结果是:
即 defer
执行先后顺序与代码顺序是相反的,是后进先出的顺序。
defer 与 return 执行顺序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package main
import "fmt"
func deferFunc() int { fmt.Println("defer func running") return 0 }
func returnFunc() int { fmt.Println("return func running") return 0 }
func TestFunc() int { defer deferFunc() return returnFunc() }
func main() { TestFunc() }
|
执行结果是:
1 2
| return func running defer func running
|
可以看到 return 语句先执行,defer 的函数后执行。
defer 影响返回值
既然 defer 执行的函数在 return 语句之后执行。那么 defer 是否可以改变函数返回值?来看下面的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package main
import "fmt"
func TestFunc() (result int) { defer func() { result = 1024 }()
return 2048
}
func main() { fmt.Println(TestFunc()) }
|
执行结果是 1024。return 的是 2048,即 defer 确实可以改变函数返回值。再看个类似的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package main
import "fmt"
func TestFunc() int { result := 1024
defer func() { result = 2048 }()
return result }
func main() { fmt.Println(TestFunc()) }
|
执行结果是 1024。这时 defer func
虽然更新了 result 的值,但并没有对函数返回值造成影响。原因是该函数的 int
类型返回值未命名(即匿名返回值),所以 defer func
无法修改该返回值,只是修改了 result 变量值。
defer 函数预计算参数值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package main
import ( "fmt" "time" )
func TestFunc() { startedAt := time.Now() defer fmt.Println(time.Since(startedAt))
time.Sleep(time.Second) }
func main() { TestFunc() }
|
这个例子是计算函数的执行时间,sleep 1s 作为模拟,然后执行结果是:199ns,显然这不是预期的结果。期望的执行时间应该是大于 1s 的。那么问题出在哪里?
由于调用 defer 时会预计算函数参数值,即预计算 time.Since(startedAt)
这个参数的值,而不是 sleep 执行完结束后计算的。可以这样改符合预期:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package main
import ( "fmt" "time" )
func TestFunc() { startedAt := time.Now() defer func() { fmt.Println(time.Since(startedAt)) }()
time.Sleep(time.Second) }
func main() { TestFunc() }
|
defer 后面跟匿名函数即可,符合我们的期望。执行结果是:1.003861399s。
defer 与 panic
触发 defer 执行的时机有三个:
- 包裹 defer 的函数遇到 return
- 包裹 defer 的函数执行到最后
- 发生 panic
来看下 defer 与 panic 相遇时会发生什么。
发生 panic 时在 defer 中不捕获异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package main
import "fmt"
func TestFunc() { defer func() { fmt.Println("A") }()
defer func() { fmt.Println("B") }()
panic("panic occurred") }
func main() { TestFunc() }
|
执行结果:
1 2 3 4
| B A panic: panic occurred ...
|
发生 panic 时在 defer 中捕获异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package main
import "fmt"
func TestFunc() { defer func() { fmt.Println("A") }()
defer func() { fmt.Println("B") if err := recover(); err != nil { fmt.Println("catch panic") } }()
panic("panic occurred") }
func main() { TestFunc() }
|
执行结果是:
这个例子执行结果没什么可说的,发生 panic,可以在 defer 函数中捕获处理。
defer 中发生 panic
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package main
import "fmt"
func TestFunc() { defer func() { if err := recover(); err != nil { fmt.Printf("catch %s panic\n", err) } }()
defer func() { panic("inner") }()
panic("outer") }
func main() { TestFunc() }
|
执行结果是:catch inner panic
。即外层发生 panic(“outer”)
,触发 defer 执行,执行 defer 中又发生 panic(“inner”)
,最后一个 defer 中捕获 panic,捕获到的是最后一个 panic。
另外,尽量不要使用 panic,仅在发生不可恢复的错误或程序启动时发生意外才使用 panic。