Baca dalam bahasa Inggris

Kontrol dengan fungsi penundaan, kepanikan, dan pemulihan

Selesai

Kini mari kita lihat beberapa alur kontrol yang unik untuk Go: defer, panic, dan recover. Masing-masing fungsi ini memiliki beberapa kasus penggunaan. Kita akan mengeksplorasi kasus penggunaan yang paling penting di sini.

Fungsi penangguhan

Di Go, pernyataan defer menunda eksekusi fungsi (termasuk parameter apa pun) hingga fungsi yang berisi pernyataan selesai defer. Umumnya, Anda menunda fungsi saat Anda ingin menghindari melupakan tugas seperti menutup file atau menjalankan proses pembersihan.

Anda dapat menangguhkan fungsi sebanyak yang Anda inginkan. Pernyataan defer berjalan dalam urutan terbalik, dari terakhir hingga pertama.

Lihat cara kerja pola ini dengan menjalankan contoh kode berikut:

Go
package main

import "fmt"

func main() {
    for i := 1; i <= 4; i++ {
        defer fmt.Println("deferred", -i)
        fmt.Println("regular", i)
    }
}

Berikut adalah output kodenya:

Output
regular 1
regular 2
regular 3
regular 4
deferred -4
deferred -3
deferred -2
deferred -1

Dalam contoh ini, perhatikan bahwa setiap kali fmt.Println("deferred", -i) ditangguhkan, nilai untuk i disimpan, dan panggilan fungsi ditambahkan ke antrean. Setelah fungsi main() selesai mencetak nilai regular, semua panggilan yang ditangguhkan berjalan. Perhatikan output dari panggilan yang ditangguhkan berada dalam urutan terbalik (terakhir masuk, pertama keluar), saat keluar dari antrean.

Kasus penggunaan umum untuk fungsi defer ini adalah menutup file setelah Anda selesai menggunakannya. Berikut contohnya:

Go
package main

import (
    "io"
    "os"
    "fmt"
)

func main() {
    newfile, error := os.Create("learnGo.txt")
    if error != nil {
        fmt.Println("Error: Could not create file.")
        return
    }
    defer newfile.Close()

    if _, error = io.WriteString(newfile, "Learning Go!"); error != nil {
	    fmt.Println("Error: Could not write to file.")
        return
    }

    newfile.Sync()
}

Setelah membuat atau membuka file, Anda menangguhkan fungsi .Close() untuk menghindari lupa menutup file setelah selesai.

Fungsi panik

Kesalahan runtime membuat program Go menjadi panik, seperti mencoba mengakses array dengan menggunakan indeks di luar batas atau mendereferensikan pointer nihil. Anda juga bisa memaksa program untuk panik.

Fungsi bawaan panic() menghentikan alur kontrol normal dalam program Go. Saat Anda menggunakan panggilan panic, setiap panggilan fungsi yang ditangguhkan berjalan secara normal. Proses melanjutkan tumpukan hingga semua fungsi kembali. Program ini kemudian crash dengan pesan log. Pesan ini mencakup informasi kesalahan dan jejak tumpukan untuk membantu Anda mendiagnosis akar penyebab masalah.

Saat menggunakan fungsi panic(), Anda dapat menambahkan nilai apa pun sebagai argumen. Biasanya, Anda mengirim pesan kesalahan tentang alasan Anda panik.

Misalnya, kode berikut menggabungkan fungsi panic dan defer. Coba jalankan kode ini untuk melihat bagaimana alur kontrol terganggu. Perhatikan bahwa proses pembersihan masih berjalan.

Go
package main

import "fmt"

func highlow(high int, low int) {
    if high < low {
        fmt.Println("Panic!")
        panic("highlow() low greater than high")
    }
    defer fmt.Println("Deferred: highlow(", high, ",", low, ")")
    fmt.Println("Call: highlow(", high, ",", low, ")")

    highlow(high, low + 1)
}

func main() {
    highlow(2, 0)
    fmt.Println("Program finished successfully!")
}

Berikut output-nya:

Output
Call: highlow( 2 , 0 )
Call: highlow( 2 , 1 )
Call: highlow( 2 , 2 )
Panic!
Deferred: highlow( 2 , 2 )
Deferred: highlow( 2 , 1 )
Deferred: highlow( 2 , 0 )
panic: highlow() low greater than high

goroutine 1 [running]:
main.highlow(0x2, 0x3)
	/tmp/sandbox/prog.go:13 +0x34c
main.highlow(0x2, 0x2)
	/tmp/sandbox/prog.go:18 +0x298
main.highlow(0x2, 0x1)
	/tmp/sandbox/prog.go:18 +0x298
main.highlow(0x2, 0x0)
	/tmp/sandbox/prog.go:18 +0x298
main.main()
	/tmp/sandbox/prog.go:6 +0x37

Program exited: status 2.

Inilah yang terjadi saat kode berjalan:

  1. Semuanya berjalan normal. Program mencetak nilai tinggi dan rendah yang diteruskan ke fungsi highlow().

  2. Ketika nilai low lebih besar dari nilai high, program akan panik. Anda melihat pesan Panic!. Pada titik ini, alur kontrol terganggu, dan semua fungsi yang ditangguhkan mulai mencetak pesan Deferred....

  3. Program akan crash, dan Anda melihat jejak tumpukan penuh. Anda tidak melihat pesan Program finished successfully!.

Panggilan ke fungsi panic() biasanya berjalan ketika kesalahan besar tidak diharapkan. Untuk menghindari crash program, Anda bisa menggunakan fungsi lain bernama recover().

Fungsi pemulihan

Kadang, Anda mungkin ingin menghindari crash program dan, sebaliknya, melaporkan kesalahan secara internal. Atau mungkin Anda ingin membersihkan kekacauan sebelum membiarkan program crash. Misalnya, Anda mungkin ingin menutup koneksi apa pun ke sumber daya guna menghindari lebih banyak masalah.

Go menyediakan fungsi bawaan recover() untuk memungkinkan Anda mendapatkan kembali kontrol setelah panik. Anda hanya memanggil recover dalam fungsi di mana Anda juga memanggil defer. Jika Anda memanggil fungsi recover(), fungsi akan mengembalikan nil dan tidak memiliki efek lain dalam eksekusi normal.

Coba ubah fungsi main dalam kode sebelumnya untuk menambahkan panggilan ke fungsi recover(), seperti ini:

Go
func main() {
    defer func() {
	handler := recover()
        if handler != nil {
            fmt.Println("main(): recover", handler)
        }
    }()

    highlow(2, 0)
    fmt.Println("Program finished successfully!")
}

Saat Anda menjalankan program, output akan terlihat seperti ini:

Output
Call: highlow( 2 , 0 )
Call: highlow( 2 , 1 )
Call: highlow( 2 , 2 )
Panic!
Deferred: highlow( 2 , 2 )
Deferred: highlow( 2 , 1 )
Deferred: highlow( 2 , 0 )
main(): recover from panic highlow() low greater than high

Program exited.

Apakah Anda melihat perbedaan dari versi sebelumnya? Perbedaan utamanya adalah Anda tidak lagi melihat kesalahan pelacakan tumpukan.

Dalam fungsi main(), Anda menugaskan fungsi anonim tempat Anda memanggil fungsi recover(). Panggilan ke recover() gagal dikembalikan nil saat program panik. Anda bisa melakukan sesuatu di sini untuk membersihkan kekacauan, tetapi dalam contoh ini, Anda cukup mencetak sesuatu.

Kombinasi fungsi panic dan recover adalah cara idiomatik Go menangani pengecualian. Bahasa pemrograman lain menggunakan blok try/catch. Pilih pendekatan yang Anda jelajahi di sini.

Untuk informasi selengkapnya, lihat proposal untuk menambahkan fungsi try bawaan di Go.