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

相關文章
相關標籤/搜索