Go Defer

Gary Liao
5 min readMay 31, 2023

--

推遲

概述

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 才是。

--

--