什麼是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
本文來自:掘金
感謝作者:li_peng
查看原文:golang 詳解 defer