Cara Menggunakan try-catch di JavaScript untuk Menangani Error

try-catch membuat satu baris kode bermasalah tidak menjatuhkan seluruh halaman. Panduan ini membahas sintaksnya, kapan dipakai, dan jebakan yang sering terlewat.

Meja kerja pengembang dengan laptop berisi editor kode dan catatan tulisan tangan

Sebuah tombol tidak merespons karena data dari server kosong. Di tempat lain, formulir berhenti bekerja karena JSON yang diurai ternyata rusak. try-catch di JavaScript memberi kamu tempat untuk menangkap kejadian seperti itu sebelum menjatuhkan bagian lain halaman. Panduan ini menjelaskan cara memakainya dengan benar, termasuk kapan sebaiknya tidak dipakai.

Kenapa Error Perlu Ditangani

Saat sebuah error dilempar dan tidak ada yang menangkapnya, eksekusi kode di fungsi itu langsung berhenti. Kalau error terjadi di dalam event handler, sisa handler tidak jalan. Kalau terjadi saat halaman dimuat, skrip setelahnya bisa ikut gagal diparsing atau dijalankan. Pengguna melihat tombol yang diam tanpa pesan apa pun.

try-catch mengubah alur itu. Kamu membungkus kode yang berisiko, lalu menentukan apa yang harus dilakukan ketika ada yang gagal: menampilkan pesan, memakai nilai cadangan, atau mencatat kejadian ke konsol.

Sintaks Dasar try-catch

Strukturnya terdiri dari blok try berisi kode yang diawasi, dan blok catch yang menerima objek error.

try {
  const data = JSON.parse(inputDariPengguna);
  render(data);
} catch (err) {
  console.error('Gagal membaca data:', err.message);
  tampilkanPesan('Data tidak valid, periksa kembali formatnya.');
}

Kalau JSON.parse gagal, baris render(data) tidak dijalankan dan kontrol langsung pindah ke catch. Objek err berisi properti name dan message yang membantu saat kamu perlu membedakan jenis kegagalan.

Memeriksa Jenis Error

Objek error punya tipe berbeda. TypeError muncul saat memanggil properti dari undefined, SyntaxError muncul dari penguraian teks yang salah format, dan RangeError muncul saat angka di luar batas yang diizinkan. Kamu bisa memeriksa err.name atau memakai instanceof untuk menangani masing-masing secara berbeda.

try {
  proses(data);
} catch (err) {
  if (err instanceof TypeError) {
    tampilkanPesan('Struktur data tidak sesuai.');
  } else {
    tampilkanPesan('Terjadi kesalahan tak terduga.');
    console.error(err);
  }
}

Perbedaan ini berguna ketika kamu ingin memberi pesan yang spesifik ke pengguna tanpa membocorkan detail teknis.

Blok finally dan Perannya

Blok finally selalu dijalankan, baik try berhasil maupun gagal. Tempat ini cocok untuk membersihkan sumber daya: menyembunyikan indikator loading, menutup koneksi, atau mengembalikan status tombol ke aktif.

tombol.disabled = true;
tampilkanLoading();

try {
  await simpanData(form);
} catch (err) {
  console.error(err);
  tampilkanPesan('Gagal menyimpan. Coba lagi.');
} finally {
  tombol.disabled = false;
  sembunyikanLoading();
}

Tanpa finally, kamu perlu menulis ulang baris pembersihan di dua tempat. Risikonya, satu jalur terlewat dan tombol tertinggal dalam keadaan nonaktif.

Melempar Error Sendiri

Fungsi buatanmu bisa memakai throw untuk menandai kondisi yang tidak boleh dilanjutkan. Ini membuat pemanggil fungsi tahu ada yang salah tanpa harus menebak dari nilai balik.

function hitungDiskon(harga, persen) {
  if (typeof harga !== 'number' || harga < 0) {
    throw new RangeError('Harga harus angka positif.');
  }
  if (persen < 0 || persen > 100) {
    throw new RangeError('Persen diskon di luar rentang.');
  }
  return harga - (harga * persen / 100);
}

Pemanggil kemudian dapat membungkusnya dengan try-catch dan menampilkan pesan dari err.message. Melempar error lebih jelas daripada mengembalikan null karena penyebabnya tidak hilang di tengah jalan.

try-catch pada Kode Asynchronous

Di fungsi async, try-catch menangkap penolakan promise yang di-await. Ini membuat alur kode terlihat berurutan dan mudah dibaca.

async function muatProfil(id) {
  try {
    const res = await fetch('/api/profil/' + id);
    if (!res.ok) throw new Error('Status ' + res.status);
    return await res.json();
  } catch (err) {
    console.error('Gagal memuat profil:', err.message);
    return null;
  }
}

Perhatikan bahwa fetch tidak melempar error untuk status 404 atau 500. Kamu perlu memeriksa res.ok sendiri, lalu melempar error kalau statusnya bukan 2xx. Kalau kamu baru mulai memakai async/await, lihat panduan cara menggunakan async/await di JavaScript dengan benar agar pola ini konsisten.

Kesalahan yang Sering Terjadi

Ada beberapa pola yang tampak aman tetapi justru menyulitkan perbaikan.

  • Menelan error tanpa jejak. Blok catch kosong membuat bug hilang dari pandangan. Minimal catat ke konsol.
  • Membungkus terlalu banyak kode dalam satu try. Kamu jadi tidak tahu baris mana yang gagal. Pisahkan bagian yang memang berisiko.
  • Memakai catch untuk mengontrol alur normal. Untuk kondisi yang bisa diperiksa, pakai percabangan biasa.
  • Mengabaikan error di dalam callback. try-catch di sekitar fungsi yang menerima callback tidak menangkap error yang terjadi belakangan saat callback dipanggil.

Untuk kasus terakhir, penanganannya bergantung pada API yang dipakai. Banyak API modern mengembalikan promise, sehingga try-catch dengan await bekerja sebagaimana mestinya.

Kapan Error Layak Dibiarkan Naik

Tidak semua error harus ditangkap di tempat kejadian. Kalau sebuah fungsi tidak tahu cara memulihkan keadaan, biarkan error naik ke lapisan yang lebih tinggi yang punya konteks, misalnya lapisan yang mengatur tampilan keseluruhan. Menangkap lalu melempar ulang dengan pesan yang lebih jelas juga praktik yang wajar.

try {
  await kirimPesanan(keranjang);
} catch (err) {
  throw new Error('Checkout gagal: ' + err.message);
}

Menghubungkan dengan Alur Debugging

Pesan error yang jelas mempersingkat waktu pencarian penyebab. Sertakan konteks yang relevan, seperti nilai id atau nama operasi, di dalam pesan. Hindari menampilkan objek mentah ke antarmuka pengguna karena isinya bisa membingungkan. Jika kamu sering berhadapan dengan stack trace yang panjang, tips pada panduan debugging JavaScript untuk pemula membantu membaca jejak error dengan lebih tenang.

Error yang tertangkap juga berguna untuk pencatatan. Kirim ringkasan ke layanan pemantauan agar kamu tahu masalah yang dialami pengguna di lapangan, tanpa harus menunggu laporan manual.

Ringkasan

try-catch membungkus kode yang berisiko, menangkap kegagalan, dan memberi kesempatan untuk memulihkan keadaan. Gunakan finally untuk pembersihan, lempar error sendiri saat input tidak valid, dan periksa res.ok pada fetch. Biarkan error yang tidak bisa ditangani di lapisan bawah naik ke tempat yang punya cukup konteks untuk menanganinya.

Pertanyaan yang Sering Diajukan

Apakah try-catch memperlambat kode JavaScript?

Biaya utamanya muncul saat error benar-benar dilempar, bukan saat blok try dimasuki. Mesin JavaScript modern menangani blok try tanpa penalti berarti pada kode normal. Kekhawatiran performa lebih relevan kalau kamu memakai error untuk alur yang sebenarnya bisa dicek dengan percabangan.

Bagaimana menangkap error di dalam setTimeout?

try-catch di sekitar pemanggilan setTimeout tidak akan menangkap error yang terjadi saat callback berjalan. Bungkus isi callback-nya dengan try-catch sendiri, atau ubah pendekatan ke promise dan gunakan await.

Apakah semua error harus ditangkap?

Tidak. Tangkap error yang bisa kamu tangani, misalnya dengan menampilkan pesan atau memakai nilai cadangan. Error yang tidak bisa dipulihkan sebaiknya dibiarkan naik agar terlihat di konsol atau sistem pemantauan, bukan disembunyikan.