golang 详解 defer

什么是defer

defer用来声明一个延迟函数,把这个函数放入到一个栈上, 当外部的包含方法return以前,返回参数到调用方法以前调用,也能够说是运行到最外层方法体的"}"时调用。咱们常常用他来作一些资源的释放,好比关闭io操做bash

func doSomething(fileName string) {
    file,err := os.Open(fileName)
    if err != nil {
    panic(err)
    }
    defer file.Close()
}
复制代码

defer 能够保证方法能够在外围函数返回以前调用。有点像其余言的 try finally闭包

try{
}finally{
}
复制代码

defer 读写外部变量

defer声明的函数读写外部变量,和闭包差很少。好比下面的代码函数

func doSomething() {
    v := 10
    defer func() {
        fmt.Println(v)
        v++
        fmt.Println(v)
    }()
    v += 5
}
复制代码

输出为ui

15
16
复制代码

就像闭包同样,若是不是defer函数方法内的变量会向上一层函数访问变量,从新作计算。spa

defer 读写命名的返回值

这个例子中,defer声明的方法,给命名的返回值自增1code

1 func doSomething() (rev int) {
2     defer func() {
3         rev++
4     }()
5 
6     return 5
7 }
复制代码

第6行return 至关于资源

return rev = 5
复制代码

defer 声明的匿名函数会在return 以前执行,至关于string

rev = 5
// 执行defer方法
rev++
//而后return
return
复制代码

因此结果是6
我把代码作一点点修改it

1 func doSomething() (rev int) {
2     v := 10
3     defer func() {
4         v++
5     }()
6 
7     return v
8 }
复制代码

第7行返回的是局部变量v.   io

return v 至关于 return rev = v
复制代码

defer 函数里是对局部变量v的操做,因此与返回的rev没有关系。
全部执行的结果是:10

defer 执行顺序

当有多个defer时执行顺序逆向的,后进先出:

func doSomething() {
    defer fmt.Println(1)
    defer fmt.Println(2)
}
复制代码

会先输出2,再输出1

defer 处理异常

  panic抛出异常后,若是不处理应用程序会崩溃。为了防止程序崩溃,咱们能够在defer的函数里使用recover来捕获中异常:

func doSomething() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Print(err)
        }
        
    }()

    fmt.Println("Running...")
    panic("run error")
}
复制代码

输出:

Running...
run error
复制代码

recover 会捕获panic的异常。我再把代码作一点点修改:

func doSomething() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Print(err)
        }
        
    }()

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

    fmt.Println("Running...")
    panic("run error")
}
复制代码

输出结果

Running...
defer error
复制代码

由于 recover()只捕获最后一次panic

相关文章
相关标签/搜索