Go語言的defer算是一個語言的新特性,至少對比當今主流編程語言如此。根據GO LANGUAGE SPEC的說法:git
A "defer" statement invokes a function whose execution is deferred to the moment the surrounding function returns, either because the surrounding function executed a return statement, reached the end of its function body, or because the corresponding goroutine is panicking. defer語句調用一個函數,這個函數執行會推遲,直到外圍的函數返回,或者外圍函數運行到最後,或者相應的goroutine panicgithub
換句話說,每當defer執行的時候,它後面的函數值(在go中函數是一個引用類型,是一等公民,能夠賦值給變量)和函數參數會被求值,可是函數不會當即調用,直到(↑)上述三種狀況發生。 這就是defer的所有內容,沒了,剩下就是defer的best practice編程
先從最簡單的開始:編程語言
func readFile(fileName string){ f,err := os.Open(fileName) if err!=nil { return } defer f.Close() var content [1024]byte f.Read(content[:]) fmt.Printf("%s",content) } func main() { readFile("test.data") }
程序輸出test.data前1024字節的內容。值得一提的是,相似這種open/close配對操做是defer的慣用法。 這個例子詮釋了上面那句話的後半段函數
"可是函數不會被調用" spa
由於若是defer後面的f.Close()
沒有延遲執行,那麼文件描述符都關閉了,就不會讀取到任何內容。code
下面這個例子即將詮釋上半段,它來自<>,稍做修改:blog
func trace(funcName string) func(){ start := time.Now() fmt.Printf("function %s enter\n",funcName) return func(){ log.Printf("function %s exit (elapsed %s)",funcName,time.Since(start)) } } func foo(){ defer trace("foo()")() time.Sleep(5*time.Second) } func main(){ foo() foo() } /* OUTPUT: function foo() enter function foo() exit (elapsed 5.0095471s) function foo() enter function foo() exit (elapsed 5.0005382s) */
爲何foo會輸出enter
而後等待五秒左右再輸出exit
? 由於正如咱們說的,字符串
defer後面的函數值和參數會被求值可是實際函數調用卻要等到最後get
這裏函數值就是trace()
返回的匿名函數,函數參數固然就是字符串字面值"foo()", 對trace("foo()")
的求值會輸出function foo() enter
, 實際函數調用trace("foo()")()
即輸出function foo() exit(elapsed x.x)
會推遲到return執行(若是return會更新返回值變量,則會在更新後才執行defer的函數)。
多說一點,若是存在多個defer語句,最後的defer的函數的執行順序與defer出現的順序相反,如:
func main() { func1 := func(){ fmt.Println("func1() execution deferred") } func2 := func(){ fmt.Println("func2() execution deferred") } defer func1() defer func2() fmt.Println("strat\nworking...") } /* OUTPUT: strat working... func2() execution deferred func1() execution deferred */