天天看點

Go 語言系列20:defer 延遲調用

含有 ​

​defer​

​​ 語句的函數,會在該函數将要傳回之前,調用另一個函數。簡單點說就是 ​

​defer​

​ 語句後面跟着的函數會延遲到目前函數執行完後再執行。

下面是一個簡單的例子:

package main

import "fmt"

func myPrint() {
 fmt.Println("Go")
}

func main() {
 defer myPrint()
 fmt.Println("Let's")
}      

首先,執行 ​

​main​

​​ 函數,因為 ​

​myPrint()​

​​ 函數前有 ​

​defer​

​​ 關鍵字,是以會在執行完 ​

​main​

​​ 函數後再執行 ​

​myPrint()​

​​ 函數,是以先列印出 ​

​Let's​

​​ ,再執行 ​

​myPrint()​

​​ 函數列印 ​

​Go​

​ 。運作該程式輸出如下:

Let's
Go      

上面的程式等價于下面的程式:

defer fmt.Println("Go")
fmt.Println("Let's")      
Go 語言系列20:defer 延遲調用

即時求值的變量快照

Go 語言系列20:defer 延遲調用

使用 ​

​defer​

​ 隻是延時調用函數,傳遞給函數裡的變量,不應該受到後續程式的影響。

str := "Go"
defer fmt.Println(str)
str = "Let's"
fmt.Println(str)      

同理,運作該程式會輸出如下:

Let's
Go      
Go 語言系列20:defer 延遲調用

延遲方法

Go 語言系列20:defer 延遲調用

​defer​

​ 不僅能夠延遲函數的執行,也能延遲方法的執行。

package main

import "fmt"

type Person struct {
 firstName, lastName string
}

func (p Person) printName() {
 fmt.Printf("%s %s", p.firstName, p.lastName)
}

func main() {
 p := Person{"John", "Smith"}
 defer p.printName()
 fmt.Printf("Hello ")
}      

運作該程式輸出如下:

Hello John Smith      
Go 語言系列20:defer 延遲調用

defer 棧

Go 語言系列20:defer 延遲調用

當一個函數内多次調用 ​

​defer​

​​ 時,Go 會把 ​

​defer​

​ 調用放入到一個棧中,随後按照 後進先出(Last In First Out, LIFO) 的順序執行。

package main

import "fmt"

func main() {
 defer fmt.Printf("Caizi.")
 defer fmt.Printf("am ")
 defer fmt.Printf("I ")
 fmt.Printf("Hello! ")
}      

運作上面的程式輸出如下:

Hello! I am Caizi.      
Go 語言系列20:defer 延遲調用

defer 在 return 後調用

Go 語言系列20:defer 延遲調用

看看下面的例子你就知道了:

package main

import "fmt"

var x int = 100

func myfunc() int {
 defer func() {x = 200}()
 fmt.Println("myfunc: x =", x)
 return x
}

func main() {
 myx := myfunc()
 fmt.Println("main: x =", x)
 fmt.Println("main: myx =", myx)
}      

運作該程式輸出如下:

myfunc: x = 100
main: x = 200
main: myx = 100      
Go 語言系列20:defer 延遲調用

defer 可以使代碼更簡潔

Go 語言系列20:defer 延遲調用

如果沒有使用 ​

​defer​

​​ ,當在一個操作資源的函數裡調用多個 ​

​return​

​ 時,每次都得釋放資源,你可能這樣寫代碼:

func f() {
    r := getResource()  //0,擷取資源
    ......
    if ... {
        r.release()  //1,釋放資源
        return
    }
    ......
    if ... {
        r.release()  //2,釋放資源
        return
    }
    ......
    if ... {
        r.release()  //3,釋放資源
        return
    }
    ......
    r.release()     //4,釋放資源
    return
}      

有了 ​

​defer​

​ 之後,你可以簡潔地寫成下面這樣:

func f() {
    r := getResource()  //0,擷取資源

    defer r.release()  //1,釋放資源
    ......
    if ... {
        ...
        return
    }
    ......
    if ... {
        ...
        return
    }
    ......
    if ... {
        ...
        return
    }
    ......
    return
}      

參考文獻:

[1] Alan A. A. Donovan; Brian W. Kernighan, Go 程式設計語言, Translated by 李道兵, 高博, 龐向才, 金鑫鑫 and 林齊斌, 機械工業出版社, 2017.

繼續閱讀