多个defer出现的时候,它会把defer之后的函数压入一个栈中延迟执行,也就是先进后出(LIFO),写在前面的defer会比写在后面的defer调用的晚。下面通过一个示例看一下:
scss
代码解读
复制代码
func func1(){
fmt.Println("我是 func1")
}
func func2(){
fmt.Println("我是 func2")
}
func func3(){
fmt.Println("我是 func3")
}
func main(){
defer func1()
defer func2()
defer func3()
fmt.Println("main1")
fmt.Println("main2")
}
先执行return的函数,得到返回值,再执行defer,最后执行ret返回。
示例:
go
代码解读
复制代码
package main
import "fmt"
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()
}
执行结果为:
return func called
defer func called
但当 return、defer 的执行顺序和函数返回值“相遇” 时,又将会产生许多复杂的场景。
在 demo4_2 中,函数使用命名返回值,最终输出结果为 7。其中经历了这几个过程:
(首先)变量 num 作为返回值,初始值为 0;
(其次)随后变量 num 被赋值为 10;
(然后)return 时,变量 num 作为返回值被重新赋值为 2;
(接着)defer 在 return 后执行,拿到变量 num 进行修改,值为 7;
(最后)变量 num 作为返回值,最终函数返回结果为 7;
func demo4_2() (num int) {
num = 10
defer func() {
num += 5
}()
return 2
}
// 7
再来看一个例子。
在 demo4_3 中,函数使用匿名返回值,最终结果输出为 2。其中经历的过程是这样的:
进入函数,此时返回值变量并未创建;
创建变量 num,赋值为 10;
return 时创建函数返回值变量,并赋值为 2;这个返回值变量你可以把它看成匿名变量,或者是变量 a、b、c、d……,但它就不是变量 num;
defer 时,无论怎样修改变量 num,都与函数返回值无关;
所以,最终的函数返回结果为 2;
func demo4_3() int {
num := 10
defer func() {
num += 5
}()
return 2
}
// 2
defer 最大的功能是 panic 后依然有效,所以defer可以保证你的一些资源一定会被关闭,从而避免一些异常出现的问题。
go
代码解读
复制代码
package main
import "fmt"
func main() {
deferPanic()
}
func deferPanic() {
defer fmt.Println("defer 1")
defer fmt.Println("defer 2")
defer fmt.Println("defer 3")
panic("出错啦")
}
执行输出如下:
defer 3
defer 2
defer 1
panic: 出错啦
defer错误处理
发生 panic 时,已声明的 defer 会出栈执行
运行 demo5_1,可以看到当出现 panic 时,会触发已经声明的 defer 出栈执行,随后在再 panic,而在 panic 之后声明的 defer 将得不到执行。
func demo5_1() {
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
panic("没点赞异常") // 触发defer出栈执行
defer fmt.Println(4) // 得不到执行
}
正是利用这个特性,在 defer 中可以通过 recover 捕获 panic,防止程序崩溃。
func demo5_2() {
defer func() {
if err := recover(); err != nil {
fmt.Println(err, "问题不大")
}
}()
panic("没点赞异常") // 触发defer出栈执行
// ...
}
panic:
1、内置函数
2、假如函数F中书写了panic语句,会终止其后要执行的代码,在panic所在函数F内如果存在要执行的defer函数列表,按照defer的逆序执行
3、返回函数F的调用者G,在G中,调用函数F语句之后的代码不会执行,假如函数G中存在要执行的defer函数列表,按照defer的逆序执行
4、直到goroutine整个退出,并报告错误
recover:
1、内置函数
2、用来控制一个goroutine的panicking行为,捕获panic,从而影响应用的行为
3、一般的调用建议
a). 在defer函数中,通过recever来终止一个goroutine的panicking过程,从而恢复正常代码的执行
b). 可以获取通过panic传递的error
1.利用recover处理panic指令,defer 必须放在 panic 之前定义,(在panic之后定义的defer不会执行),另外 recover 只有在 defer 调用的函数中才有效。否则当panic时,recover无法捕获到panic,无法防止panic扩散。
2.recover 处理异常后,逻辑并不会恢复到 panic 那个点去,函数跑到 defer 之后的那个点。
3.多个 defer 会形成 defer 栈,后定义的 defer 语句会被最先调用。
package main
func main() {
test()
}
func test() {
defer func() {
if err := recover(); err != nil {
println(err.(string)) // 将 interface{} 转型为具体类型。
}
}()
panic("panic error!")
}
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered:", r)
}
}()
panic("oh no!") // 触发 panic
fmt.Println("This line will not be executed.")
recover 处理异常后,逻辑并不会恢复到 panic 那个点去,函数跑到 defer 之后的那个点。
package main
import (
"fmt"
)
func test() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered:", r)//4
}
fmt.Println("Returned normalily from main.")//5
}()
fmt.Println("Starting main function.")//1
panicExample()
}
func panicExample() {
defer fmt.Println("Deferred call in panicExample.")
fmt.Println("Starting panicExample.")//2
panic("Oh no! Something went wrong.")//3
fmt.Println("This line will not be executed.")
}
func main() {
test()
fmt.Println("This line will not be executed.")//6
}
捕获函数 recover 只有在延迟调用内直接调用才会终止错误,否则总是返回 nil。任何未捕获的错误都会沿调用堆栈向外传递。
package main
import "fmt"
func test() {
defer func() {
fmt.Println(recover()) //有效
}()
defer recover() //无效!
defer fmt.Println(recover()) //无效!
defer func() {
func() {
println("defer inner")
recover() //无效!
}()
}()
panic("test panic")
}
func main() {
test()
}
使用延迟匿名函数或下面这样都是有效的。
package main
import (
"fmt"
)
func except() {
fmt.Println(recover())
}
func test() {
defer except()
panic("test panic")
}
func main() {
test()
}
Go实现类似 try catch 的异常处理
package main
import "fmt"
func Try(fun func(), handler func(interface{})) {
defer func() {
if err := recover(); err != nil {
handler(err)
}
}()
fun()
}
func main() {
Try(func() {
panic("test panic")
}, func(err interface{}) {
fmt.Println(err)
})
}
输出结果:
test panic
如何区别使用 panic 和 error 两种方式?
惯例是:导致关键流程出现不可修复性错误的使用 panic,其他使用 error。
标准库 errors.New 和 fmt.Errorf 函数用于创建实现 error 接口的错误对象。通过判断错误对象实例来确定具体错误类型。
package main
import (
"errors"
"fmt"
)
var ErrDivByZero = errors.New("division by zero")
func div(x, y int) (int, error) {
if y == 0 {
return 0, ErrDivByZero
}
return x / y, nil
}
func main() {
defer func() {
fmt.Println(recover())
}()
switch z, err := div(10, 0); err {
case nil:
println(z)
case ErrDivByZero:
panic(err)
}
}
输出结果:
division by zero
defer与闭包
package main
import (
"errors"
"fmt"
)
func foo(a, b int) (i int, err error) {
defer fmt.Printf("first defer err %v\n", err)//这里的err获取时还为空
defer func(err error) { fmt.Printf("second defer err %v\n", err) }(err)//这里的err获取时还为空
//只有这个是闭包
defer func() { fmt.Printf("third defer err %v\n", err) }()
if b == 0 {
err = errors.New("divided by zero!")
return
}
i = a / b
return
}
func main() {
foo(2, 0)
}
输出结果:
third defer err divided by zero!
second defer err <nil>
first defer err <nil>
解释:如果 defer 后面跟的不是一个 closure 最后执行的时候我们得到的并不是最新的值。
defer 在声明时,就已经确认了形参 n 的值,而不是在执行时确认的;所以,后续变量 num 无论如何改变都不影响 defer 的输出结果。
func demo1() {
for i := 0; i < 5; i++ {
defer fmt.Println("defer:", i)
}
}
//没形成闭包,defer压栈时直接传入i值
// defer: 4
// defer: 3
// defer: 2
// defer: 1
// defer: 0
释放两种资源
package main
import (
"fmt"
"io"
"os"
)
func do() error {
f, err := os.Open("book.txt")
if err != nil {
return err
}
if f != nil {
defer func(f io.Closer) {
if err := f.Close(); err != nil {
fmt.Printf("defer close book.txt err %v\n", err)
}
}(f)
}
// ..code...
f, err = os.Open("another-book.txt")
if err != nil {
return err
}
if f != nil {
defer func(f io.Closer) {
if err := f.Close(); err != nil {
fmt.Printf("defer close another-book.txt err %v\n", err)
}
}(f)
}
return nil
}
func main() {
do()
}
javascript
代码解读
复制代码
package main
import "fmt"
func function(index int, value int) int {
fmt.Println(index)
return index
}
func main() {
defer function(1, function(3, 0))
defer function(2, function(4, 0))
这个程序的执行结果是怎么样的的?
3
4
2
1
参考:
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- yule263.com 版权所有 湘ICP备2023023988号-1
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务