Program JavaScript yang berjalan nyaris pasti menemui error, baik karena data dari luar tidak sesuai harapan maupun karena kode menyentuh nilai yang tidak ada. Topik ini menata cara menghadapinya, mulai dari try/catch/finally yang menampung error, jenis-jenis Error bawaan, kelas Error kustom untuk kesalahan yang punya nama sendiri, sampai dua jalur penanganan error pada kode asinkron. Setelah membacanya, pola penanganan yang rapi dapat dipakai ulang di mana saja, alih-alih membiarkan program berhenti dengan pesan yang tidak dijelaskan.
Alur try, catch, dan finally#
Blok try membungkus kode yang berpotensi gagal, catch menampung error yang dilempar dari blok try itu, dan finally dijalankan setelah keduanya, baik saat error tertangkap maupun tidak. Kata kunci throw melontarkan error, dan nilai yang dilontarkan sebaiknya selalu object turunan Error karena object itu membawa informasi standar, yaitu name, message, dan stack. Ketika throw dijalankan, eksekusi blok try berhenti dan langsung melompat ke catch terdekat.
function bacaFile(nama) {
try {
if (nama === "kosong") {
throw new Error("berkas kosong");
}
return "isi berkas";
} catch (error) {
console.log("ditangkap: " + error.message);
return "isi cadangan";
} finally {
console.log("finally tetap berjalan");
}
}
console.log(bacaFile("kosong"));
console.log(bacaFile("normal"));ditangkap: berkas kosong
finally tetap berjalan
isi cadangan
finally tetap berjalan
isi berkasKeluaran di atas memperlihatkan sifat finally yang paling sering dimanfaatkan, yaitu blok ini tetap berjalan meskipun catch sudah mengembalikan nilai. Blok finally pun dipakai untuk pekerjaan pembersihan yang tidak boleh terlewat, misalnya menutup koneksi atau menghentikan pengukuran waktu. Perlu dicatat bahwa variabel error pada catch (error) hanya hidup di dalam bloknya, sehingga dua blok catch yang berbeda boleh memakai nama yang sama. Properti error.stack juga tersedia bila ingin menelusuri urutan pemanggilan fungsi yang berujung pada error, meskipun isinya memuat jalur berkas yang panjang.
Jenis Error bawaan#
JavaScript menyediakan beberapa jenis Error bawaan sehingga jenis kesalahan dapat dibaca langsung dari properti name tanpa menebak dari pesannya. Empat jenis yang paling sering muncul pada program harian dirangkum di tabel berikut.
| Jenis | Pemicu umum | Contoh pesan |
|---|---|---|
TypeError | Mengakses properti pada null atau undefined, atau memanggil nilai yang bukan fungsi | Cannot read properties of null |
ReferenceError | Memakai nama variabel yang tidak pernah dideklarasikan | ... is not defined |
RangeError | Nilai berada di luar rentang yang sah | Invalid array length |
SyntaxError | Teks yang diurai tidak membentuk kode yang sah | Expected property name |
function uji(tugas) {
try {
tugas();
} catch (error) {
console.log(error.name + ": " + error.message);
}
}
uji(() => null.halo);
uji(() => variabelYangTidakAda);
uji(() => new Array(-1));
uji(() => JSON.parse("{salah}"));TypeError: Cannot read properties of null (reading 'halo')
ReferenceError: variabelYangTidakAda is not defined
RangeError: Invalid array length
SyntaxError: Expected property name or '}' in JSON at position 1 (line 1 column 2)Setiap baris keluaran menampilkan jenis error dan pesannya berdampingan, sehingga sumber masalah terlihat tanpa membaca stack trace, yaitu daftar urutan pemanggilan fungsi saat error dilontarkan. Satu batasan penting menyertai SyntaxError pada tabel di atas. Error pada contoh muncul ketika program sudah berjalan, karena JSON.parse mengurai teksnya saat itu, sehingga try/catch sempat menangkapnya. Kesalahan sintaks pada kode program sendiri terjadi lebih awal, yaitu ketika berkas sedang diurai, sehingga seluruh berkas gagal dijalankan sebelum blok try mana pun sempat dieksekusi.
Kelas Error kustom#
Kelas Error kustom lahir dari extends Error, sehingga jenis kesalahan tertentu dapat dikenali dari kelasnya, bukan dari isi pesannya. Konstruktor kelas kustom memanggil super(pesan) agar properti standar terisi, lalu menetapkan this.name supaya jenisnya tidak lagi tertulis “Error”, dan bebas menambahkan properti lain yang dibutuhkan, misalnya nama kolom yang gagal divalidasi. Berkas berikut memuat demo lengkapnya dan dapat dijalankan langsung dengan node validasi.js.
// Kelas Error kustom untuk kesalahan validasi form.
class ValidationError extends Error {
constructor(kolom, pesan) {
super(pesan);
this.name = "ValidationError";
this.kolom = kolom;
}
}
function validasi(form) {
if (!form.nama) {
throw new ValidationError("nama", "kolom nama wajib diisi");
}
if (form.umur < 0 || form.umur > 120) {
throw new ValidationError("umur", "umur harus di antara 0 dan 120");
}
return true;
}
function periksa(form) {
try {
validasi(form);
console.log("form valid: " + form.nama);
} catch (error) {
if (error instanceof ValidationError) {
console.log("validasi gagal (" + error.kolom + "): " + error.message);
} else {
throw error;
}
}
}
periksa({ nama: "Citra", umur: 17 });
periksa({ nama: "", umur: 17 });
periksa({ nama: "Budi", umur: 500 });form valid: Citra
validasi gagal (nama): kolom nama wajib diisi
validasi gagal (umur): umur harus di antara 0 dan 120Pola instanceof pada kode di atas memisahkan dua jenis error yang berbeda penanganannya. ValidationError adalah keluaran yang diharapkan dari proses validasi, sehingga cukup dicetak ringkas untuk pengguna. Error lain yang bukan ValidationError dilempar lagi dengan throw error, karena kesalahan itu kemungkinan besar adalah bug, dan menyembunyikannya justru memindahkan masalah ke tempat yang lebih sulit ditelusuri.
Dua jalur error asinkron#
Error pada kode asinkron tidak bisa ditangkap try/catch biasa yang ditulis di luar, karena hasilnya baru muncul pada waktu lain setelah blok try selesai dijalankan. Kegagalan promise disebut rejection, dan rejection punya dua jalur penanganan yang setara, yaitu .catch di ujung rantai then atau try/catch yang mengelilingi await. Keduanya menerima object error yang sama persis, sehingga pilihannya menjadi soal gaya kode yang sedang ditulis.
function ambilRapor(siswa) {
return new Promise((resolve, reject) => {
if (siswa === "Budi") {
reject(new RangeError("nilai siswa Budi belum tercatat"));
return;
}
resolve({ nama: siswa, nilai: 90 });
});
}
async function jalankan() {
// jalur pertama: catch pada rantai then
await ambilRapor("Budi")
.then((rapor) => console.log("nilai: " + rapor.nilai))
.catch((error) => console.log("jalur then: " + error.name + " - " + error.message));
// jalur kedua: try/catch pada await
try {
const rapor = await ambilRapor("Budi");
console.log("nilai: " + rapor.nilai);
} catch (error) {
console.log("jalur await: " + error.name + " - " + error.message);
}
}
jalankan();jalur then: RangeError - nilai siswa Budi belum tercatat
jalur await: RangeError - nilai siswa Budi belum tercatatDiagram berikut merangkum dua jalur itu beserta jalur ketiga yang justru paling berbahaya, yaitu rejection yang tidak ditangani siapa pun.
Rejection yang dibiarkan bukan peringatan yang bisa diabaikan. Sejak Node.js 15, unhandled rejection menghentikan proses Node secara langsung, sedangkan pada browser muncul pesan “Uncaught (in promise)” di konsol. Di sisi lain, satu .catch di ujung rantai menampung error dari seluruh mata rantai di depannya, termasuk error yang dilontarkan dari dalam callback then itu sendiri, sehingga rantai yang panjang pun cukup ditutup satu kali.
Ringkasan#
- Blok
trymenampung kode yang berpotensi gagal,catchmenerima object errornya, danfinallyselalu dijalankan sehingga cocok untuk pekerjaan pembersihan. - Jenis
Errorbawaan sepertiTypeError,ReferenceError,RangeError, danSyntaxErrormenyebut jenis kesalahan lewat propertiname. - Kelas
Errorkustom dariextends Errormembawa properti tambahan dan dikenali lewatinstanceof, sedangkan error yang tidak dikenali dilempar lagi. - Pada kode asinkron, rejection ditangkap
.catchdi ujung rantaithenatautry/catchdi sekitarawait, dan rejection tanpa penanganan menghentikan proses Node sejak Node.js 15.
Referensi#
- try…catch — MDN Web Docs — Aturan try, catch, finally, dan variabel error pada catch.
- Error — MDN Web Docs — Properti standar Error dan daftar jenis Error bawaan.
- async function — MDN Web Docs — Perilaku await dan cara error promise dilontarkan ke try/catch.
- Promise.prototype.catch() — MDN Web Docs — Penanganan rejection pada rantai then.