golang 詳解defer


什么是defer

defer用來聲明一個延遲函數,把這個函數放入到一個棧上, 當外部的包含方法return之前,返回參數到調用方法之前調用,也可以說是運行到最外層方法體的"}"時調用。我們經常用他來做一些 資源的釋放,比如關閉io操作
 
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
}

輸出為

15
16

  就像閉包一樣,如果不是defer函數方法內的變量會向上一層函數訪問變量,重新做計算。

defer 讀寫命名的返回值

    這個例子中,defer聲明的方法,給命名的返回值自增1
 
1 func doSomething() (rev int) {
2     defer func() {
3         rev++
4     }()
5 
6     return 5
7 }

  第6行的return 相當於

return rev = 5

  defer 聲明的匿名函數會在return 之前執行,相當於

rev = 5
// 執行defer方法
rev++
//然后return
return

  所以結果是6

  我把代碼做一點點修改

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

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

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM