Kontrol dengan fungsi penundaan, kepanikan, dan pemulihan
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:
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:
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:
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.
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:
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:
Semuanya berjalan normal. Program mencetak nilai tinggi dan rendah yang diteruskan ke fungsi
highlow()
.Ketika nilai
low
lebih besar dari nilaihigh
, program akan panik. Anda melihat pesanPanic!
. Pada titik ini, alur kontrol terganggu, dan semua fungsi yang ditangguhkan mulai mencetak pesanDeferred...
.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:
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:
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.