推遲
概述
defer <function call>
陳述式可以進行 function call,但被 call 的 function 會推遲到外層 function returns 時才執行。
在許多情境下,他可以充當 Golang 實現解構子的工具,但 defer
的彈性更大。
特性
- 被推遲的function,我們稱為 deferred function。
- 外層function,我們稱之為 surrounding function。
func df(){ }
func sf(){ // surrounding function
defer df() // (o) deferred function
defer (df()) // (x) deferred function cannot be parenthesized
}
在 defer <function call>
陳述式被執行時,deferred function本身,以及傳入的參數,都會被存成一個當下的快照。
假如有多個deferred functions,那他們最後會以先進後出的順序被執行。
精確地說,deferred function 的執行發生在外層 function 的 return value 產生之後,但真正被回傳之前,因此,deferred function 可以修改 surrounding function 的 return value,如果 scope 允許的話。
範例
package main
import (
"fmt"
)
func sf() (result int) {
df := func(i int) {
fmt.Println("defered function executed:", i)
result += i
}
for i := 0; i <= 3; i++ {
defer df(i)
fmt.Println("defered function called:", i)
}
return 6
}
func main() {
fmt.Println(sf()) // 12
}
結果:
defered function called: 0
defered function called: 1
defered function called: 2
defered function called: 3
defered function executed: 3
defered function executed: 2
defered function executed: 1
defered function executed: 0
12
應用場景
關閉檔案或 conn
package main
import (
"fmt"
"os"
)
func main() {
filenames := []string{"a.txt", "b.txt", "c.txt"}
for _, filename := range filenames {
if err := doFile(filename); err != nil {
fmt.Println(err)
}
}
}
func doFile(filename string) error {
f, err := os.Open(filename)
if err != nil {
return err
}
defer f.Close()
// ... do the file
return nil
}
注意這邊把 file open 包到另一個function內,是因為若直接在迴圈內執行 file open,所有的 file 都會在最後才被 close。
計算 function 執行時間
package main
import (
"log"
"time"
)
func main() {
bigSlowOperation()
}
func bigSlowOperation() {
defer trace("bigSlowOperation")() // don't forget the extra parentheses
// ...lots of work...
time.Sleep(10 * time.Second) // simulate slow operation by sleeping
}
func trace(msg string) func() {
start := time.Now()
log.Printf("enter %s", msg)
return func() { log.Printf("exit %s (%s)", msg, time.Since(start)) }
}
// 2023/05/31 06:25:55 enter bigSlowOperation
// 2023/05/31 06:26:05 exit bigSlowOperation (10.0097151s)
這個案例很特殊的是 trace 本身並不是 deferred function,而 trace 回傳的 function 才是。