Golang_defer执行顺序

Golang defer

https://blog.meowrain.cn/api/i/2025/01/26/mooQ6t1737905697985818701.avif

Golang defer 执行时机

defer 语句在声明时,会立即计算其参数的值并将函数与参数绑定,但函数体的执行会延迟到外层函数即将返回时。

如果在函数中有多个 defer,它们会按照后进先出的顺序执行,类似栈结构

看下面的例子

https://blog.meowrain.cn/api/i/2025/01/26/NTQVDX1737906325105278946.avif

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main

import "fmt"

func example() {
	defer fmt.Println("First")
	defer fmt.Println("Second")
	fmt.Println("Inner")
}

func main() {
	example()
}

defer 和匿名函数的一些问题和疑惑

直接声明 defer 匿名函数,参数不通过匿名函数的参数传入

这个其实就是 匿名函数捕获外部变量(闭包行为):

  • 匿名函数会捕获变量的引用(即变量地址),而不是变量值。
  • 如果变量在 defer 注册之后发生了变化,匿名函数使用的值也会随之变化。

https://blog.meowrain.cn/api/i/2025/01/26/0JmiRW1737905820857614459.avif

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package main

import "fmt"

func main() {
	x := 10
	defer func() {
		fmt.Println(x)
	}()
	x += 20

}

像上面这个例子,我们的 x 不是通过参数从匿名函数传入的,所以对于 x 这个值的获取,是在程序 return 前才获取的,也就是 30

直接声明 defer 匿名函数,参数通过匿名函数的参数传入

这个就是传入参数立即求值: 如果在 defer 调用时直接传递参数,那么这些参数会在 defer 声明时立即计算并存储。

https://blog.meowrain.cn/api/i/2025/01/26/OjfT6x1737906146997736935.avif

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package main

import "fmt"

func main() {
	x := 10
	defer func(x int) {
		fmt.Println(x)
	}(x)
	x += 20

}

与 return 的关系

1.函数遇到 retrun 的时候,会先记录返回值,然后执行 defer 语句,最后再真正返回

当函数遇到 return 时,会先记录返回值(如果有),然后执行 defer 语句,最后再真正返回。

例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package main

import "fmt"

func main() {
    result := example()
    fmt.Println("Main function received:", result)
}

func example() int {
    defer fmt.Println("Deferred statement executed")

    fmt.Println("Function is executing")
    return 42
}

https://blog.meowrain.cn/api/i/2025/01/26/H2cQof1737906465265266347.avif

解释:

  1. example 函数开始执行,打印 "Function is executing"
  2. 遇到 return 42 时,函数会先记录返回值 42,但不会立即返回。
  3. 接着,defer 语句 fmt.Println("Deferred statement executed") 被执行,打印 "Deferred statement executed"
  4. 最后,函数真正返回,main 函数接收到返回值 42 并打印 "Main function received: 42"

这个例子展示了 defer 语句在 return 之后、函数真正返回之前执行的特性。

2. 可以在 defer 函数中修改返回值

如果函数有具名返回值,defer 中可以修改返回值。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package main

import "fmt"

func example() (result int) {
	result = 10
	defer func() {
		result = 50
	}()
	return result
}
func main() {
	r := example()
	fmt.Println(r)
}

https://blog.meowrain.cn/api/i/2025/01/26/U0aY1N1737906635713305213.avif

defer 异常捕获与处理

通过 defer 结合 recover 函数,可以在发生异常时进行恢复操作,并获取异常信息进行处理。例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package main

import "fmt"

func divide(a, b int) {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("received panic from divide: ", r)
		}
	}()
	fmt.Printf("%d / %d = %d\n", a, b, a/b)
}
func main() {
	divide(1, 0)

}

https://blog.meowrain.cn/api/i/2025/01/26/GRtglU1737906827274567659.avif

底层机制

https://blog.meowrain.cn/api/i/2025/01/27/QWivc61737907312832016664.avif

面试题

https://blog.meowrain.cn/api/i/2025/01/26/GohTfX1737906929019902860.avif

形成了闭包,这个匿名函数会捕获我们的变量 x 的引用,defer 会在 return 保存值后执行,所以这个时候 x 已经变成 x+1 了,所以 print 打印的值是 x + 1

如果把 x 从匿名函数的参数传入,那么就不会形成闭包,这些参数会在 defer 声明时立即计算并存储,这样的话,打印出来就还是 x 了


相关内容

0%