//defer 作用域在当前函数和方法返回之前被调用
// return 比 defer 更先执行
package main
import "fmt"
func deferFunc() int {
fmt.Println("defer func done")
return 0
}
func returnFunc() int {
fmt.Println("return func done")
return 0
}
func returnAndDefer() int {
defer deferFunc()
return returnFunc()
}
func main() {
returnAndDefer()
}
$ go run main.go
return func done
defer func done
package main
import "fmt"
func main() {
{
defer fmt.Println("defer done")
fmt.Println("code block done")
}
fmt.Println("main done")
}
// go run main.go
// code block done
// main done
// defer done
// defer 执行顺序,先进后出
package main
import "fmt"
func main() {
defer funcA()
defer funcB()
defer funcC()
}
func funcA() {
fmt.Println("A")
}
func funcB() {
fmt.Println("B")
}
func funcC() {
fmt.Println("C")
}
$ go run main.go
C
B
A
// defer 影响主函数的具名返回值
// 当主函数有返回值,且返回值有没有名字没有关系,defer所作用的函数,即defer可能会影响主函数的返回值
package main
import "fmt"
func main() {
fmt.Println(deferFunc())
fmt.Println("main done")
}
func deferFunc() (j int) {
i := 1
defer func() {
j++
}()
return i
}
// go run main.go
// 2
func main() {
fmt.Println(deferFuncReturn())
}
func deferFuncReturn() int {
i := 1
defer func() {
i++
}()
return i
}
$ go run main.go
1
结论:当主函数有返回值 ,会在函数初始化时赋值为0,且其作用域为该函数全域,defer 会影响到该返回值。
defer 偶遇 panic
在 Golang 中,执行过程中遇到 panic 错误时,遍历所有defer,强行 defer 出栈,并执行 defer。在执行过程中,
遇到 recover 捕获异常停止 panic,返回 recover 继续执行
未设置 recover 捕获异常,遍历完 defer 抛出 panic 信息
1.defer 未捕获 panic
package main
import (
"fmt"
)
func main() {
defer_call()
fmt.Println("main done...")
}
func defer_call() {
defer func() {
fmt.Println("defer func 1")
}()
defer func() {
fmt.Println("defer func 2")
}()
// 触发defer出
panic("error1")
defer func() {
fmt.Println("defer func 3: no exec")
}()
}
$ go run main.go
defer func 2
defer func 1
panic:error1
... 堆栈error...
main.defer_call()
/Users/dip/go/src/prc-gorm/main.go:22 +0x4e
main.main()
/Users/dip/go/src/prc-gorm/main.go:8 +0x13
exit status 2
2.defer 捕获 panic
package main
import (
"fmt"
)
func main() {
defer_call()
fmt.Println("main done...")
}
func defer_call() {
defer func() {
fmt.Println("defer func 1, 捕获异常")
if err := recover(); err != nil {
fmt.Println(err, "333")
}
}()
defer func() {
fmt.Println("defer func 2, 没有捕获异常")
}()
// 触发defer出
panic("error11111")
defer func() {
fmt.Println("defer func 3: no exec")
}()
}
(base) dip@DipdeMacBook-Pro prc-gorm % go run main.go
defer func 2, 没有捕获异常
defer func 1, 捕获异常
error11111 333
main done...
3 defer 中含有 panic
Golang 中,panic 仅会被最后一个 revover 捕获。
package main
import (
"fmt"
)
func main() {
defer func() {
if err := recover(); err != nil{
fmt.Println("err:", err)
}else {
fmt.Println("fatal")
}
}()
defer func() {
panic("defer panic2")
}()
panic("panic1")
}
$ go run main.go
err: defer panic2
在上面例子中,panic("panic1")先 触发 defer 强制出栈,第一个执行触发 panic("defer panic2)"异常,此时会覆盖前一个异常 panic,最后继续执行 defer, 最终被 recover()捕获住