• defer 的执行顺序
  • defer 与 return
  • defer 与 函数返回值
  • defer 与 panic
  • defer 中包含 panic
  • defer 下的函数参数包含子函数

defer 的执行顺序

栈,先进后出

func func1() {
    fmt.Println("A")
}

func func2() {
    fmt.Println("B")
}

func func3() {
    fmt.Println("C")
}

func main() {
    defer func1()
    defer func2()
    defer func3()
}
// result:
// C
// B
// A

defer 与 return

在retrun之后,函数结束之前执行; 因为 return 可以包含一些操作,而不是单纯地返回某个值,所以 defer需要在return之后执行

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()
}
// result:
// return func called
// defer func called

defer 与 函数返回值

  1. 函数的返回值会被初始化为对应类型的零值;
  2. return 是将"2"这个值赋值给t,函数最终返回的是t的值, 不是"2"。(虽然最终t=2)
  3. 当defer执行的函数带有形参时,取当前环境的变量
func withDeferRes(i int) (res int) {
	// !!!注意!!! defer压栈时,如果有形参则需要在压栈时得到确定的值后传入(当前环境的变量)
	defer func(a int) {
		res = a
	}(i)

	// 此时修改i并不会影响之后执行defer
	i += 100
	fmt.Println("i = ", i)

	// res 为int型的零值:0
	fmt.Println("res = ", res)

	// return将 “2” 赋值给 res, 最终函数返回的是res的值(res会被defer影响)
	return 2
}

func withDeferRes2(i int) (res int) {
	// 没带形参i
	defer func() {
		res = i
	}()

	// !!!注意!!! 此时修改i会影响之后执行defer
	i += 100
	fmt.Println("i = ", i)

	// res 为int型的零值:0
	fmt.Println("res = ", res)

	// return将 “2” 赋值给 res, 最终函数返回的是res的值(res会被defer影响)
	return 2
}

func withDefer3(n1, n2 int) int {
    // Println函数存在参数,取当前环境变量n1,不会被后面的 n1++ 影响
	defer fmt.Println("n1's value: ", n1)
	defer fmt.Println("n2's value:", n2)

	n1++
	n2++

	return n1 + n2
}

func returnButDefer() (t int) {
	// 没有形参,defer中的t会被影响
	defer func() {
		t = t * 10
	}()

	// 将 “1” 这个值传给t,影响defer
	return 1
}



func main() {
	withDeferRes(10)
	// result:
	// i =  110
	// res =  0

	fmt.Println(withDeferRes(12))
	// i =  112
	// res =  0
    // 12
    
    withDeferRes2(20)
	// result:
	// i =  120
	// res =  0

	fmt.Println(withDeferRes2(22))
	// i =  122
	// res =  0
    // 122
    
    withDefer3(99, 999)
	// n2's value: 999
    // n1's value:  99
    
    fmt.Println(returnButDefer())
	// 10
}

defer 与 panic

遇到 panic 时,遍历本协程的 defer 链表,并执行 defer 。保证你的一些资源一定会被关闭。

  1. 没有遇到 recover,遍历完本协程的 defer 链表后,向 stderr 抛出 panic 信息。
func defer_call() {

	defer func() {
		fmt.Println("defer: panic 之前 1")
	}()

	defer func() {
		fmt.Println("defer: panic 之前 2")
	}()

	panic("异常内容") //触发 defer 出栈

	defer func() {
		fmt.Println("defer: panic 之后,永远执行不到")
	}()
}

func main() {
	defer_call()

	fmt.Println("main 正常结束")
}

// result:
// defer: panic 之前 2
// defer: panic 之前 1
// panic: 异常内容
// ... 异常堆栈信息
  1. 如果在执行 defer 过程中,遇到 recover 则停止 panic,返回 recover 处继续往下执行。
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 之后, 永远执行不到")
	}()
}

func main() {
	defer_call()

	fmt.Println("main 正常结束")
}
// result:
// defer: panic 之前 2, 不捕获
// defer: panic 之前 1, 捕获异常
// 异常内容
// main 正常结束
  1. 多个panic,仅有最后一个会被recover()捕获
func main() {

	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		} else {
			fmt.Println("fatal")
		}
	}()

	defer func() {
		panic("defer panic")
	}()

	panic("panic")
}
// result:
// defer panic

defer 下的函数参数包含子函数

压栈时需要连同函数地址、函数形参一同进栈。形参如果是个函数则被调用。

func function(index int, value int) int {
	fmt.Println(index)
	return index
}

//result:  3 1 4 2
func noDefer() {
	function(1, function(3, 0))
	function(2, function(4, 0))
}

// result: 3 4 2 1
func withDefer() {
	// 压栈时有形参,需要先执行function(3, 0), 得到确定的值后再压栈 function(2,3)
	defer function(1, function(3, 0))
	// 压栈时有形参,需要先执行function(4, 0), 得到确定的值后再压栈 function(2,4)
	defer function(2, function(4, 0))

}

//result: 3 1 4 2
func withDefer2() {
	// 压栈时没参数
	defer func() {
		function(1, function(3, 0))
		function(2, function(4, 0))
	}()

}

func main() {
	noDefer()
	// result:  3 1 4 2

	withDefer()
	// result: 3 4 2 1

	withDefer2()
	// result: 3 1 4 2
}