panic, recover และ defer
เรียนรู้ว่าเมื่อใดควรใช้ panic และวิธีกู้คืนการทำงานอย่างเหมาะสม
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: ทำงานเมื่อฟังก์ชันสิ้นสุดลง (ไม่ว่าจะปกติ เกิดข้อผิดพลาด หรือเกิดแพนิก) โดยทำงานตามลำดับ LIFOrecover: หยุดแพนิกและส่งคืนค่า โดยต้องอยู่ภายใน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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- interface error
- การสร้างข้อผิดพลาดแบบกำหนดเอง
- การห่อและคลายข้อผิดพลาด
- panic, recover และ defer