Go Academy · บทเรียน

panic, recover และ defer

เรียนรู้ว่าเมื่อใดควรใช้ panic และวิธีกู้คืนการทำงานอย่างเหมาะสม

บทเรียน 4 จาก 412 ขั้นตอน

panic, recover และ defer เป็นบทเรียน Go Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Go Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Go Academy มีบทเรียนทั้งหมด 4 บทเรียน

แพนิกคืออะไร

panic จะหยุดการทำงานปกติของโกโรทีนปัจจุบัน คลายลำดับการเรียกใช้พร้อมเรียกฟังก์ชันที่เลื่อนการทำงานไว้ และทำให้โปรแกรมหยุดทำงานหากไม่มีการกู้คืน:

package main
import "fmt"

func mustPositive(n int) int {
    if n <= 0 {
        panic(fmt.Sprintf("mustPositive: got %d", n))
    }
    return n
}

func main() {
    fmt.Println(mustPositive(5))  // 5
    // fmt.Println(mustPositive(-1)) // panics!
}

เมื่อใดควรใช้แพนิก

ใช้ panic เฉพาะในกรณีต่อไปนี้:

  • ข้อผิดพลาดของผู้เขียนโปรแกรมที่ไม่สามารถกู้คืนได้อย่างแท้จริง (เงื่อนไขคงเดิมถูกทำลาย)
  • เงื่อนไขที่เป็นไปไม่ได้ซึ่งบ่งบอกว่ามีบั๊ก
  • ความล้มเหลวระหว่างเริ่มต้น (การกำหนดค่าหายไป หรือการเชื่อมต่อล้มเหลวระหว่างเริ่มต้น)

อย่าใช้แพนิกกับเงื่อนไขความล้มเหลวที่คาดหมายไว้ — ให้ใช้ error แทน

พื้นฐานของ defer

defer กำหนดให้เรียกฟังก์ชันเมื่อฟังก์ชันที่ครอบอยู่คืนค่า ไม่ว่าจะคืนค่าในลักษณะใด (ปกติ เกิดข้อผิดพลาด หรือเกิดแพนิก):

package main
import "fmt"

func main() {
    defer fmt.Println("third")
    defer fmt.Println("second")  // LIFO order
    defer fmt.Println("first")
    fmt.Println("running...")
    // Output:
    // running...
    // first
    // second
    // third
}

การใช้ defer เพื่อล้างทรัพยากร

การใช้งาน defer ที่พบบ่อยที่สุดคือการทำให้แน่ใจว่าทรัพยากรได้รับการล้างอย่างเหมาะสม:

package main
import ("fmt"; "os")

func readFile(path string) (string, error) {
    f, err := os.Open(path)
    if err != nil {
        return "", fmt.Errorf("open: %w", err)
    }
    defer f.Close()  // always runs, even if we return early

    buf := make([]byte, 1024)
    n, _ := f.Read(buf)
    return string(buf[:n]), nil
}

recover — การดักจับแพนิก

recover หยุดแพนิกและคืนค่าของแพนิก โดยต้องเรียกโดยตรงภายในฟังก์ชัน defer:

package main
import "fmt"

func safeDiv(a, b int) (result int, err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("recovered from panic: %v", r)
        }
    }()
    return a / b, nil  // panics if b==0
}

func main() {
    r, err := safeDiv(10, 0)
    fmt.Println(r, err)
    // 0 recovered from panic: runtime error: integer divide by zero
}

recover ต้องอยู่ในฟังก์ชันที่เลื่อนการทำงานไว้

recover จะทำงานก็ต่อเมื่อเรียกโดยตรงภายในฟังก์ชันที่เลื่อนการทำงานไว้ การเรียกที่อื่นจะคืนค่า nil:

package main
import "fmt"

func wrong() {
    // recover() here does nothing — not in defer
}

func correct() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("caught:", r)
        }
    }()
    panic("oops")
}

func main() { correct() }

แพนิกในโค้ดไลบรารี

โค้ดไลบรารีแทบไม่ควรปล่อยให้แพนิกหลุดไปถึงผู้เรียกใช้ ให้แปลงแพนิกเป็นข้อผิดพลาดที่ขอบเขต:

package main
import ("fmt"; "runtime/debug")

func safeCall(fn func()) (err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("panic: %v\n%s", r, debug.Stack())
        }
    }()
    fn()
    return nil
}

func main() {
    err := safeCall(func() { panic("boom") })
    fmt.Println(err != nil) // true
}

ลำดับ LIFO ของ defer และข้อควรระวังในลูป

การเรียกที่เลื่อนการทำงานไว้จะทำงานตามลำดับเข้าหลังออกก่อน (LIFO) โปรดระวังการเลื่อนการทำงานไว้ภายในลูป — ให้ใช้ฟังก์ชันปิดหรือฟังก์ชันช่วย:

package main
import "fmt"

func processFiles(names []string) {
    for _, name := range names {
        name := name  // capture loop variable
        defer func() { fmt.Println("close:", name) }()
    }
}

func main() {
    processFiles([]string{"a.txt", "b.txt", "c.txt"})
    // Output (LIFO): close: c.txt, close: b.txt, close: a.txt
}

defer กับค่าที่คืนชื่อไว้

ฟังก์ชันที่เลื่อนการทำงานไว้สามารถอ่านและแก้ไขค่าที่คืนชื่อไว้ได้ ซึ่งมีประโยชน์สำหรับการใส่คำอธิบายให้ข้อผิดพลาด:

package main
import "fmt"

func doWork() (err error) {
    defer func() {
        if err != nil {
            err = fmt.Errorf("doWork: %w", err)
        }
    }()
    return fmt.Errorf("something failed")
}

func main() {
    err := doWork()
    fmt.Println(err) // doWork: something failed
}

แพนิกเทียบกับข้อผิดพลาด: แนวทางตัดสินใจ

การเลือกระหว่างแพนิกกับข้อผิดพลาด:

  • ใช้ ข้อผิดพลาด: ความล้มเหลวที่คาดหมายไว้ (ไม่พบไฟล์ หมดเวลาการเชื่อมต่อเครือข่าย ข้อมูลนำเข้าไม่ถูกต้อง)
  • ใช้ แพนิก: ข้อผิดพลาดในการเขียนโปรแกรม (การเข้าถึงแผนที่ที่ไม่มีค่า การเข้าถึงนอกขอบเขต) สถานะที่เป็นไปไม่ได้ และความล้มเหลวระหว่างเริ่มต้น
  • อย่าใช้แพนิกข้ามขอบเขตส่วนติดต่อการใช้งาน — ผู้เรียกใช้ไม่สามารถจัดการได้
  • กู้คืนแพนิกที่ระดับบนสุดของโกโรทีนในเซิร์ฟเวอร์ เพื่อหลีกเลี่ยงการหยุดทำงาน

ตรวจสอบอย่างรวดเร็ว

ต้องเรียก recover() ที่ใดจึงจะดักจับแพนิกได้

สรุปทบทวน: panic, recover, defer

สรุป:

  • panic: คลายสแตก เรียกใช้ฟังก์ชันเลื่อนเวลา และทำให้โปรแกรมหยุดทำงานหากไม่มีการกู้คืน
  • defer: ทำงานเมื่อฟังก์ชันสิ้นสุดลง (ไม่ว่าจะปกติ เกิดข้อผิดพลาด หรือเกิดแพนิก) โดยทำงานตามลำดับ LIFO
  • recover: หยุดแพนิกและส่งคืนค่า โดยต้องอยู่ภายใน defer
  • ใช้แพนิกสำหรับข้อผิดพลาดจากโปรแกรมเมอร์ ไม่ใช่ความล้มเหลวที่คาดหมายได้
  • แปลงแพนิกเป็นข้อผิดพลาดที่ขอบเขตของส่วนติดต่อโปรแกรม
เริ่มต้นได้ฟรี

เรียนรู้ Go ด้วย AI tutor — ฟรี

เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป

คอร์ส
51
บทเรียน
203

คำถามที่พบบ่อย

บทเรียน “panic, recover และ defer” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “panic, recover และ defer” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Go Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Go Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “panic, recover และ defer”

เรียนรู้ว่าเมื่อใดควรใช้ panic และวิธีกู้คืนการทำงานอย่างเหมาะสม คุณปฏิบัติ Go Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Go Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Go Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน

บทเรียน “panic, recover และ defer” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Go Academy นี้ได้ไหม

ได้ บทเรียน Go Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. interface error
  2. การสร้างข้อผิดพลาดแบบกำหนดเอง
  3. การห่อและคลายข้อผิดพลาด
  4. panic, recover และ defer
← กลับไปที่ Go Academy