Chapter sebelumnya selalu berakhir sama: throw yang tidak ditangkap menghentikan seluruh program. Untuk satu entri konfigurasi yang rusak, program pengolah data berhenti total — padahal entri lain masih layak diproses. Perilaku seperti ini tidak dapat kita terima pada aplikasi nyata, di mana input berasal dari pengguna, berkas, atau jaringan dan kapan pun bisa saja tidak sempurna.
try-catch adalah alat untuk itu: kita menandai blok kode yang berisiko, menyediakan penangkapnya, dan memutuskan apa yang dilakukan setelah galat muncul. Blok finally melengkapi pasangan itu dengan jaminan pembersihan. Chapter ini membahas ketiganya, termasuk dua perilaku halus: cakupan variabel error dan urutan return dengan finally.
Menangkap Galat dengan try dan catch #
Aturannya: kode yang berisiko melempar galat ditulis di dalam blok try. Bila galat terlempar di dalamnya — oleh baris kita sendiri maupun oleh function yang dipanggil — eksekusi langsung lompat ke blok catch, dan program melanjutkan dari sana. Bila blok try selesai tanpa galat, blok catch dilewati.
Berkas berikut mem-parsing tiga string JSON. Yang kedua sengaja tidak sah, tetapi program tidak berhenti karena galatnya tertangkap:
// baca-konfigurasi.js
// try-catch: galat ditangkap, sisa program tetap berjalan.
const pengaturan = [
'{ "tema": "gelap", "ukuran": 14 }',
'{ tema: gelap }',
'{ "bahasa": "id" }',
];
for (const teks of pengaturan) {
try {
const data = JSON.parse(teks);
console.log("OK - terbaca: " + JSON.stringify(data));
} catch (error) {
console.log("Gagal - " + error.name + ": " + error.message);
}
}
console.log("Program selesai dengan normal");Output yang Dihasilkan:
OK - terbaca: {"tema":"gelap","ukuran":14}
Gagal - SyntaxError: Expected property name or '}' in JSON at position 2 (line 1 column 3)
OK - terbaca: {"bahasa":"id"}
Program selesai dengan normalMari kita telaah baris-baris pentingnya:
- Baris 11 (
try {): blok mulai ditandai. Engine menyiapkan penangkap untuk galat apa pun yang terlempar di dalam blok ini. - Baris 12 (
const data = JSON.parse(teks);):JSON.parsemelemparSyntaxErrorketika stringnya bukan JSON sah. Tanpatry, galat ini akan menghentikan program — persis kejadian pada chapter Syntax dan Runtime Errors. - Baris 14 (
} catch (error) {): bila galat terlempar, object galat diterima di parametererror, dan blok ini berjalan menggantikan sisa bloktry. - Baris 15 (
console.log("Gagal - " + error.name + ": " + error.message);): alih-alih berhenti, kegagalan dicatat sebagai satu baris keluaran, lalu loop berlanjut ke entri berikutnya. - Baris 19 (
console.log("Program selesai dengan normal");): baris ini tercapai — bukti utama bahwa galat tertangkap tidak menghentikan program.
Perhatikan entri ketiga tetap terbaca {"bahasa":"id"} meski entri kedua gagal. Satu kegagalan lokal kini berdampak lokal pula.
Yang ditangkap catch tidak terbatas galat yang dilempar baris di blok try itu sendiri. Galat dari function mana pun yang dipanggil di dalamnya — JSON.parse, atau function buatan kita yang di dalamnya melempar — ikut tertangkap, karena eksekusi menyusuri rantai pemanggil ke bawah dan berhenti di penangkap terdekat. Mekanisme inilah yang kelak menjadi fondasi pola lapisan pada chapter Error Boundaries in Application Design.
Satu batasan penting perlu dicatat sejak awal:
Struktur bloknya juga punya aturan: try tidak boleh berdiri sendiri, minimal didampingi catch atau finally. Ketiadaan keduanya ditolak parser:
try {
console.log("isi try");
}SyntaxError: Missing catch or finally after tryVariabel error Hanya Hidup di catch #
Parameter catch (error) mengikuti aturan scope dari section Functions: variabel itu hanya ada di dalam blok catch. Di luarnya, error tidak dikenal. Jika kebetulan ada variabel bernama sama di luar, variabel tersebut tidak tersentuh — parameter catch menaunginya hanya selama blok berjalan:
let error = "milik luar";
try {
JSON.parse("{ oops }");
} catch (error) {
console.log("Dalam catch:", error.name);
}
console.log("Di luar catch:", error);Output yang Dihasilkan:
Dalam catch: SyntaxError
Di luar catch: milik luarDi dalam catch, error merujuk object galat; setelah blok selesai, nama itu kembali menunjuk string "milik luar". Di sini konvensi penamaan ikut membantu: parameter catch (error) adalah pilihan umum, dan banyak kode memakai nama singkat catch (e) bila bloknya pendek. Apa pun namanya, jangan lupa bahwa nama itu hanya sah di dalam bloknya.
Bila detail galat memang tidak dibutuhkan, parameter boleh ditiadakan sepenuhnya:
try {
JSON.parse("{ oops }");
} catch {
console.log("Tetap tertangkap, tanpa nama variabel");
}Output yang Dihasilkan:
Tetap tertangkap, tanpa nama variabelfinally: Selalu Dijalankan #
Struktur lengkapnya terdiri dari tiga blok berurutan — try, lalu catch, lalu finally. catch dan finally sama-sama opsional, tetapi urutannya tidak bisa ditukar: finally selalu paling akhir, dan minimal salah satu dari keduanya wajib ada. Blok ketiga ini berisi pekerjaan yang harus tuntas apa pun hasil blok try: berhasil, gagal tertangkap, atau bahkan gagal tanpa penangkap.
Bukti paling teliti datang dari kombinasi dengan return. Perhatikan urutan keluaran pada berkas berikut:
// urutan-return.js
// Bukti urutan: finally berjalan sebelum function benar-benar kembali.
function bacaBagian(gagal) {
try {
if (gagal) {
throw new Error("data tidak lengkap");
}
console.log("try: data siap");
return "dari try";
} catch (error) {
console.log("catch: " + error.message);
return "dari catch";
} finally {
console.log("finally: selalu dijalankan");
}
}
console.log(bacaBagian(false));
console.log("---");
console.log(bacaBagian(true));Output yang Dihasilkan:
try: data siap
finally: selalu dijalankan
dari try
---
catch: data tidak lengkap
finally: selalu dijalankan
dari catchUrutannya konsisten pada dua jalur:
- Baris 9–10 (
console.log("try: data siap");lalureturn "dari try";): jalur sukses.returndi baris 10 menyatakan niat kembali — tetapi function belum benar-benar kembali. - Baris 15 (
console.log("finally: selalu dijalankan");): blokfinallyberjalan sebelum nilaireturndi antar ke pemanggil. Karena itufinally: selalu dijalankantercetak lebih dulu, barudari try. - Baris 7 (
throw new Error("data tidak lengkap");): jalur gagal. Eksekusi lompat kecatch, yang mencatat pesan dan menyiapkanreturnmiliknya. - Baris 15 lagi:
finallytetap berjalan di jalur ini — inilah jaminannya, tidak peduli jalur mana yang ditempuh.
Bentuk paling umum dari finally adalah pembersihan: koneksi ditutup, berkas ditutup, timer dihentikan. Isi finally dipilih dengan satu ujian sederhana: pekerjaan itu harus tuntas pada semua jalur — sukses maupun gagal — agar tidak ada sumber daya yang tertinggal terbuka. Snippet berikut membuktikan bahwa pembersihan tetap terjadi meski galatnya diteruskan ke atas (karena tidak ada catch di function):
const log = [];
function prosesData(gagal) {
log.push("koneksi dibuka");
try {
if (gagal) {
throw new Error("data tidak ditemukan");
}
log.push("data dibaca");
} finally {
log.push("koneksi ditutup");
}
}
try {
prosesData(true);
} catch (error) {
console.log("Ditangkap di luar:", error.message);
}
console.log(log);Output yang Dihasilkan:
Ditangkap di luar: data tidak ditemukan
[ 'koneksi dibuka', 'koneksi ditutup' ]Galat data tidak ditemukan tidak tertangkap di dalam prosesData, sehingga naik ke pemanggil — tetapi array log menunjukkan "koneksi ditutup" sempat dicatat lebih dulu. finally berjalan dahulu, barulah galat diteruskan. Bandingkan bila finally ditiadakan: log hanya berisi 'koneksi dibuka', dan pembersihan tidak pernah terjadi.
Jebakan: return di finally #
Karena finally berjalan pada saat terakhir sebelum function kembali, return di dalamnya akan menggeser nilai yang sudah disiapkan blok try. Berkas berikut membuktikannya secara nyata:
// return-di-finally.js
// Demonstrasi: return di finally menggeser hasil yang dikembalikan.
function tentukanBonus() {
try {
console.log("try: bonus Rp50000");
return 50000;
} finally {
console.log("finally: bonus diganti Rp10000");
return 10000;
}
}
console.log("Bonus diterima:", tentukanBonus());Output yang Dihasilkan:
try: bonus Rp50000
finally: bonus diganti Rp10000
Bonus diterima: 10000Nilai 50000 sudah tertahan di return milik try, tetapi return 10000 milik finally menimpanya — pemanggil menerima 10000. Lebih parah lagi, return di finally juga menelan galat yang sedang diteruskan: galat yang belum tertangkap diam-diam tidak sampai ke pemanggil. Perilaku itu membuat kegagalan nyata terlihat seperti tidak terjadi apa-apa.
Sampai di sini kita sudah memakai error.name dan error.message berulang kali. Chapter berikutnya membedah benda galat itu sendiri — apa saja yang tersimpan di dalamnya dan jenis-jenis galat bawaan yang perlu dikenali.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
baca-konfigurasi.js
Unduh berkas// baca-konfigurasi.js
// try-catch: galat ditangkap, sisa program tetap berjalan.
const pengaturan = [
'{ "tema": "gelap", "ukuran": 14 }',
'{ tema: gelap }',
'{ "bahasa": "id" }',
];
for (const teks of pengaturan) {
try {
const data = JSON.parse(teks);
console.log("OK - terbaca: " + JSON.stringify(data));
} catch (error) {
console.log("Gagal - " + error.name + ": " + error.message);
}
}
console.log("Program selesai dengan normal");
return-di-finally.js
Unduh berkas// return-di-finally.js
// Demonstrasi: return di finally menggeser hasil yang dikembalikan.
function tentukanBonus() {
try {
console.log("try: bonus Rp50000");
return 50000;
} finally {
console.log("finally: bonus diganti Rp10000");
return 10000;
}
}
console.log("Bonus diterima:", tentukanBonus());
urutan-return.js
Unduh berkas// urutan-return.js
// Bukti urutan: finally berjalan sebelum function benar-benar kembali.
function bacaBagian(gagal) {
try {
if (gagal) {
throw new Error("data tidak lengkap");
}
console.log("try: data siap");
return "dari try";
} catch (error) {
console.log("catch: " + error.message);
return "dari catch";
} finally {
console.log("finally: selalu dijalankan");
}
}
console.log(bacaBagian(false));
console.log("---");
console.log(bacaBagian(true));