5. 延迟调用
语句defer
用来延迟一个方法或者函数的执行。被延迟的方法或者函数直到当前函数执行结束前才会被执行,因此,defer
常用于资源释放、解除锁定、错误处理等操作。
5.1 defer的几个注意点
- 被
defer
的函数只是延迟了调用的时间,但是函数参数的传递是在代码执行到函数所在的那一行时发生的,如果函数传入的参数是一个函数,那么会立即执行。
func main(){
x,y := 1,2
defer func(a int){ // 代码执行到这一行时,已经将开辟了变量a的内存,并将x的值拷贝进去,之后x的改变与它无关
fmt.Println("defer x,y = ",a,y)
}(x)
x += 100
y += 200
fmt.Println(x,y)
}
/*
执行结果为:
101 202
defer x,y = 1 202
*/
但函数传入的参数是一个函数时,会先执行该函数。
func a(s string)string{
fmt.Println("a",s)
return s
}
func b(s string){
fmt.Println("b",s)
}
func main(){
defer b(a("abc"))
fmt.Println("***")
}
/*
执行结果为:
a abc
***
b abc
*/
- 多个延迟按照FILO的顺序执行。
func a(){
fmt.Println("被defer的第一个函数!")
}
func b(){
fmt.Println("被defer的第二个函数!")
}
func c(){
fmt.Println("被defer的第三个函数!")
}
func main(){
defer a()
defer b()
defer c()
}
/*
执行结果为:
被defer的第三个函数!
被defer的第二个函数!
被defer的第一个函数!
*/
return
和panic
都会终止当前函数流程,引发延迟调用。另外,return
会先更新返回值。
func test()(z int){
defer func(){
fmt.Println("defer",z)
z += 100
}()
return 200
}
func main(){
fmt.Println("test",test())
}
/*
执行结果:
defer 200
test 300
*/
当上述代码执行到test()
函数时,首先会为返回值变量z
开辟内存空间,然后将匿名函数defer
,return
语句先更新返回值,将200
放入变量z
的内存,在其返回前的一瞬间会执行被defer
的匿名函数,故执行结果的第一行为200
,随后z
自增,然后return
。
但是,如果返回值没有被命名的话,情况会有所不同,看下面代码:
func test()(int){
var z int
defer func(){
fmt.Println("defer",z)
z += 100
}()
z = 200
return z
}
func main(){
fmt.Println("test",test())
}
/*
执行结果:
defer 200
test 200
*/
可以看到,上面的代码中并没有给函数的返回值命名,故在一开始执行test()
函数时,并不会为返回值开辟内存。在执行函数体代码的过程中,先是定义了z
变量,并为其分配内存,然后defer
匿名函数,然后给z
赋值,将其返回,重点是:在执行return
语句更新返回值时,会新开辟一块内存,并将200
写入该内存,然后执行被defer
的匿名函数,此时,匿名函数中z
变量的自增已经不会影响到函数的返回值了,因为z
变量与返回值之间没有任何关联。故最后test()
函数的返回值也是200
。
当程序运行遭遇panic
时,只有其中所有被defer
的函数执行完成之后,该panic
才会真正的扩展至调用函数,在panic
之后的代码将不会再执行。
func a(){
fmt.Println("第一个被defer的函数!")
}
func b(){
fmt.Println("第二个被defer的函数!")
}
func c(){
fmt.Println("第三个被defer的函数!")
}
func d(){
fmt.Println("第四个被defer的函数!")
}
func main(){
defer a()
defer b()
panic("pppppanic!")
defer c()
defer d()
}
/*
执行结果:
第二个被defer的函数!
第一个被defer的函数!
panic: pppppanic!
*/