Like Share Discussion Bookmark Smile

J.J. Huang   2025-03-05   Getting Started Golang 04.函式   瀏覽次數:次   DMCA.com Protection Status

Go | 函式中的延遲執行技巧

💬 簡介

在 Go 語言中,defer 是一個強大的關鍵字,能夠讓某些函式延遲執行至包含該 defer 語句的函式返回之前。
這種特性讓 defer 在資源管理、錯誤處理、流程控制等場景中發揮重要作用。

本文將深入探討 defer 的特性與應用,幫助你更靈活地運用 defer 來提升程式的可讀性與安全性。

圖片來源:Gophers


🔎 defer 基礎概念

1️⃣ defer 的基本語法

defer 允許將某個函式的執行延遲到當前函式即將返回時才執行。

  • 範例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    package main

    import "fmt"

    func main() {
    fmt.Println("開始執行")
    defer fmt.Println("這是延遲執行的內容")
    fmt.Println("結束執行")
    }
    輸出結果:
    1
    2
    3
    開始執行
    結束執行
    這是延遲執行的內容

    📝 defer 會延遲執行到 main 函式即將結束時才執行。


🚀 defer 的應用場景

1️⃣ 確保資源釋放

在處理檔案、資料庫連線等資源時,defer 可以確保即使程式中途出錯,資源仍能正確釋放。

  • 範例:使用 defer 關閉檔案
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    package main

    import (
    "fmt"
    "os"
    )

    func main() {
    file, err := os.Open("example.txt")
    if err != nil {
    fmt.Println("開啟檔案失敗:", err)
    return
    }
    defer file.Close() // 確保在函式結束時關閉檔案

    fmt.Println("正在處理檔案...")
    }

    📝 defer file.Close() 確保即使函式提早返回,檔案仍然會被正確關閉。

2️⃣ 多個 defer 語句的執行順序

當函式中有多個 defer 語句時,它們會按照「後進先出」的順序執行(類似堆疊的結構)。

  • 範例:多個 defer 的執行順序
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    package main

    import "fmt"

    func main() {
    defer fmt.Println("第一個延遲執行")
    defer fmt.Println("第二個延遲執行")
    defer fmt.Println("第三個延遲執行")
    fmt.Println("主程式執行")
    }
    輸出結果:
    1
    2
    3
    4
    主程式執行
    第三個延遲執行
    第二個延遲執行
    第一個延遲執行

    📝 defer 採用 後進先出 (LIFO) 順序執行。

3️⃣ defer 在錯誤處理中的應用

defer 常與 recover() 配合使用,確保程式能在 panic 發生時進行適當的處理。

  • 範例:捕獲 panic 並優雅恢復
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    package main

    import "fmt"

    func main() {
    defer handlePanic()
    fmt.Println("開始執行...")
    panic("發生錯誤!")
    fmt.Println("這行程式碼不會執行")
    }

    func handlePanic() {
    if r := recover(); r != nil {
    fmt.Println("捕獲到 panic:", r)
    }
    }
    輸出結果:
    1
    2
    開始執行...
    捕獲到 panic: 發生錯誤!

    📝 recover() 用來捕獲 panic,避免程式異常終止。

🚨 後續文章將會提到 panicrecover()


⚠️ 注意事項

📌 defer 與函式參數的求值時機

defer 語句中的函式參數會在 defer 被呼叫時 就計算,而不是在函式真正執行時才計算。

  • ✅ 範例:函式參數的求值時機
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    package main

    import "fmt"

    func main() {
    x := 10
    defer fmt.Println("延遲執行的 x:", x)
    x = 20
    fmt.Println("即時執行的 x:", x)
    }
    輸出結果:
    1
    2
    即時執行的 x: 20
    延遲執行的 x: 10

    📝 defer fmt.Println("延遲執行的 x:", x) 時,x 的值已經固定為 10,即使後面 x 變成 20,輸出仍是 10。


🎯 總結

  • defer 用於延遲執行函式,直到外層函式返回前執行。
  • defer 適用於 資源釋放(如關閉檔案、解鎖互斥鎖等)。
  • 多個 defer後進先出 (LIFO) 執行。
  • defer 可與 recover() 搭配,用於錯誤處理。
  • defer 內部函式的參數會 defer 語句執行時 就被求值。

掌握 defer,可以讓你的 Go 程式碼更 安全、整潔,並有效管理資源。

最後建議回顧一下 Go | 菜鳥教學 目錄,了解其章節內容。


註:以上參考了
Go