Bab 9: Error Handling
9.1 try...catch
Bayangin kamu lagi masak. Kadang telurnya jatuh, kadang garamnya kebanyakan. Kalau kamu gak siap-siap, dapur bisa berantakan. try...catch itu kayak kamu bilang: "Aku coba masak ini, tapi kalau gagal, aku punya rencana cadangan."
Konsep Dasar
Biasanya kalau ada error, script langsung mati. Dengan try...catch, kita bisa "tangkap" error dan lakukan sesuatu yang lebih berguna.
try {
// Kode yang mungkin error
let hasil = JSON.parse('{ json rusak }');
} catch (err) {
// Kalau error, masuk sini
console.log("Ada error:", err.message);
}
console.log("Script tetap jalan!"); // Ini tetap dieksekusiCara Kerjanya
- Kode di dalam
try {...}dijalankan - Kalau gak ada error →
catchdilewati - Kalau ada error →
tryberhenti, langsung loncat kecatch
// Contoh tanpa error
try {
console.log("Mulai"); // (1) jalan
console.log("Selesai"); // (2) jalan
} catch (err) {
console.log("Catch diabaikan"); // gak jalan
}
// Contoh dengan error
try {
console.log("Mulai"); // (1) jalan
variabelGakAda; // ERROR!
console.log("Selesai"); // (2) GAK jalan
} catch (err) {
console.log("Error ditangkap:", err.message); // (3) jalan
}⚠️ Jebakan: try...catch Hanya untuk Runtime Error
// INI GAK BISA DITANGKAP (syntax error)
try {
{{{{ // Kode rusak secara sintaks
} catch (err) {
// Gak akan sampai sini
}
// INI BISA DITANGKAP (runtime error)
try {
variabelGakAda; // ReferenceError saat runtime
} catch (err) {
console.log(err.name); // "ReferenceError"
}Kenapa? Karena JavaScript baca kode dulu (parse), baru jalankan (run). Kalau kodenya rusak secara sintaks, dia gak bisa dibaca sama sekali.
⚠️ Jebakan: try...catch Bekerja Sinkron
// INI GAK BISA DITANGKAP
try {
setTimeout(function() {
variabelGakAda; // Error di sini gak ketangkap!
}, 1000);
} catch (err) {
console.log("Gak akan jalan");
}
// SOLUSI: Taruh try...catch DI DALAM callback
setTimeout(function() {
try {
variabelGakAda;
} catch (err) {
console.log("Sekarang ketangkap!"); // ✅
}
}, 1000);Kenapa? Karena try...catch sudah selesai duluan sebelum setTimeout dijalankan.
Objek Error
Saat error terjadi, JavaScript bikin objek error dengan properti:
try {
variabelGakAda;
} catch (err) {
console.log(err.name); // "ReferenceError"
console.log(err.message); // "variabelGakAda is not defined"
console.log(err.stack); // Stack trace (untuk debugging)
}name— Nama tipe error (ReferenceError, TypeError, SyntaxError, dll)message— Pesan penjelasan errorstack— Urutan panggilan fungsi saat error terjadi
Throw — Lempar Error Sendiri
Kadang kita perlu bikin error sendiri. Misalnya data JSON valid tapi gak lengkap:
let json = '{ "umur": 30 }'; // Gak ada nama!
try {
let user = JSON.parse(json); // Gak error, JSON-nya valid
if (!user.nama) {
throw new Error("Data gak lengkap: gak ada nama");
}
console.log(user.nama);
} catch (err) {
console.log("Error:", err.message);
// "Error: Data gak lengkap: gak ada nama"
}JavaScript punya beberapa constructor error bawaan:
throw new Error("Pesan umum");
throw new SyntaxError("Format salah");
throw new ReferenceError("Variabel gak ada");
throw new TypeError("Tipe salah");Rethrowing — Lempar Ulang Error yang Gak Dikenal
Prinsipnya: catch hanya tangani error yang kamu kenal, lempar ulang yang lain.
let json = '{ "umur": 30 }';
try {
let user = JSON.parse(json);
if (!user.nama) {
throw new SyntaxError("Data gak lengkap: gak ada nama");
}
fungsiGakAda(); // Error tak terduga!
} catch (err) {
if (err instanceof SyntaxError) {
console.log("Error Data:", err.message); // Tangani ini
} else {
throw err; // Lempar ulang yang lain!
}
}Kenapa penting? Kalau kamu tangkap semua error tanpa cek, bug tersembunyi jadi susah dilacak.
try...catch...finally
finally selalu jalan, mau ada error atau tidak:
try {
console.log("Mulai proses...");
// throw new Error("Ups!"); // Coba uncomment ini
console.log("Proses selesai");
} catch (err) {
console.log("Error:", err.message);
} finally {
console.log("Bersih-bersih..."); // SELALU jalan
}Kapan pakai finally?
function bacaData() {
let koneksi = bukaKoneksi();
try {
let data = koneksi.baca();
return data;
} catch (err) {
console.log("Gagal baca:", err.message);
return null;
} finally {
koneksi.tutup(); // SELALU tutup koneksi, mau sukses atau gagal
}
}finally jalan bahkan kalau ada return di dalam try atau catch!
Global Error Handler
Untuk error yang gak tertangkap sama sekali:
// Di Browser
window.onerror = function(message, url, line, col, error) {
console.log(`Error: ${message} di ${url}:${line}`);
};
// Di Node.js
process.on('uncaughtException', function(err) {
console.log('Error gak tertangkap:', err.message);
});Buat fungsi bacaJSON(str) yang:
- Parse string JSON
- Cek apakah ada properti
namadanumur - Kalau gak ada, throw error yang sesuai
- Kalau JSON-nya rusak, tangkap error dan kembalikan pesan yang ramah
function bacaJSON(str) {
// Kode kamu di sini
}
// Test:
console.log(bacaJSON('{ "nama": "Budi", "umur": 25 }'));
// { nama: "Budi", umur: 25 }
console.log(bacaJSON('{ "nama": "Budi" }'));
// "Error: Data gak lengkap: gak ada umur"
console.log(bacaJSON('json rusak'));
// "Error: Format JSON tidak valid"9.2 Custom Error — Bikin Class Error Sendiri
Di rumah sakit, ada banyak jenis penyakit. Dokter gak cuma bilang "sakit" — mereka bilang "demam", "patah tulang", "alergi". Begitu juga di kode, kita butuh jenis-jenis error yang spesifik supaya bisa ditangani dengan tepat.
Kenapa Perlu Custom Error?
// Tanpa custom error — semua error sama aja
try {
// ...
} catch (err) {
// Error apa ini? Gak jelas...
console.log(err.message);
}
// Dengan custom error — bisa dibedakan
try {
// ...
} catch (err) {
if (err instanceof ValidationError) {
console.log("Data gak valid:", err.message);
} else if (err instanceof NetworkError) {
console.log("Jaringan bermasalah:", err.message);
}
}Membuat Custom Error
class ValidationError extends Error {
constructor(message) {
super(message); // Panggil constructor Error
this.name = "ValidationError"; // Set nama
}
}
// Pakai
function cekUser(user) {
if (!user.nama) {
throw new ValidationError("Nama wajib diisi");
}
if (!user.umur) {
throw new ValidationError("Umur wajib diisi");
}
}
try {
cekUser({ umur: 25 });
} catch (err) {
console.log(err.name); // "ValidationError"
console.log(err.message); // "Nama wajib diisi"
console.log(err instanceof ValidationError); // true
console.log(err instanceof Error); // true
}Inheritance — Error Lebih Spesifik
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
class PropertyRequiredError extends ValidationError {
constructor(property) {
super(`Properti "${property}" wajib ada`);
this.name = "PropertyRequiredError";
this.property = property;
}
}
// Pakai
function bacaUser(json) {
let user = JSON.parse(json);
if (!user.nama) {
throw new PropertyRequiredError("nama");
}
if (!user.umur) {
throw new PropertyRequiredError("umur");
}
return user;
}
try {
bacaUser('{ "umur": 25 }');
} catch (err) {
if (err instanceof ValidationError) {
console.log(err.message); // 'Properti "nama" wajib ada'
console.log(err.property); // "nama"
}
}Tips: Auto-set this.name
Biar gak perlu tulis this.name di setiap class:
class MyError extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name; // Otomatis ambil nama class!
}
}
class ValidationError extends MyError {}
class DatabaseError extends MyError {}
console.log(new ValidationError("test").name); // "ValidationError"
console.log(new DatabaseError("test").name); // "DatabaseError"Wrapping Exception — Bungkus Error
Kalau fungsi bisa throw banyak jenis error, bungkus jadi satu:
class ReadError extends Error {
constructor(message, cause) {
super(message);
this.name = "ReadError";
this.cause = cause; // Simpan error aslinya
}
}
function bacaData(json) {
let data;
try {
data = JSON.parse(json);
} catch (err) {
throw new ReadError("Gagal parse JSON", err);
}
try {
validasi(data);
} catch (err) {
throw new ReadError("Gagal validasi", err);
}
return data;
}
// Sekarang cukup cek satu jenis error
try {
bacaData('{json rusak}');
} catch (err) {
if (err instanceof ReadError) {
console.log(err.message); // "Gagal parse JSON"
console.log(err.cause.message); // Detail error asli
}
}Kenapa wrapping? Kode pemanggil gak perlu tau detail internal. Cukup tau "ada error baca data" — kalau mau detail, cek err.cause.
Buat hierarki error untuk aplikasi toko online:
AppError— base class (extends Error, auto-set name)ValidationError— untuk input gak validNotFoundError— untuk item gak ditemukanAuthError— untuk masalah autentikasi
// Kode kamu di sini
// Test:
let err1 = new ValidationError("Email gak valid");
console.log(err1.name); // "ValidationError"
console.log(err1 instanceof AppError); // true
console.log(err1 instanceof Error); // true
let err2 = new NotFoundError("Produk gak ditemukan");
console.log(err2.name); // "NotFoundError"
console.log(err2 instanceof AppError); // trueSudah paham materi ini?
Tandai sebagai selesai untuk melacak progress-mu.