Error Handling di JavaScript: Try, Catch, Finally

Created at by Aris Munandar

Error handling adalah fondasi penting dalam pengembangan JavaScript. Tanpa teknik penanganan error yang baik, aplikasi bisa crash, UI bisa freeze, dan pengguna bisa mengalami pengalaman yang buruk. Artikel ini membahas secara mendalam cara menangani error di JavaScript, mulai dari dasar try catch finally hingga penanganan error pada Promise dan Async/Await.

Agar mudah dipahami pemula, artikel ini disusun per bagian, lengkap dengan contoh, ilustrasi, dan best practice yang dapat langsung diterapkan.

Baca juga: Asynchronous JavaScript: Callback, Promise, dan Async Await

Pengenalan Error Handling di JavaScript

Error handling di JavaScript adalah mekanisme untuk mendeteksi, menangkap, dan menangani error agar aplikasi tetap berjalan meskipun terjadi kesalahan. Dalam JavaScript, error bisa muncul karena berbagai hal: logika yang salah, nilai yang tidak sesuai, kesalahan sintaks, sampai masalah asynchronous seperti request API yang gagal.

Error handling memungkinkan developer:

  • Menghindari aplikasi crash total
  • Memberikan pesan error yang lebih ramah pengguna
  • Melacak sumber error dengan jelas (stack trace)
  • Memastikan proses tertentu tetap berjalan menggunakan finally

Dengan kata lain, error handling adalah “sabuk pengaman” dalam pemrograman JavaScript.

Kenapa Error Handling Penting

Tanpa error handling, sebuah error kecil bisa menghentikan seluruh jalannya aplikasi. Dalam aplikasi modern—terutama yang berjalan di browser—ini bisa sangat fatal.

Beberapa alasan utama kenapa error handling penting:

1. Menjaga pengalaman pengguna

Error yang tidak ditangani dapat membuat halaman kosong, tombol tidak bekerja, atau form gagal terkirim tanpa penjelasan. Dengan error handling, Anda bisa menampilkan pesan seperti:

“Oops, terjadi kesalahan. Coba lagi nanti.”

2. Mencegah aplikasi berhenti total

JavaScript menghentikan eksekusi suatu fungsi ketika error muncul. Jika tidak ditangani, error itu dapat menyebar ke bagian lain.

3. Membantu debugging

Dengan menangkap error, Anda bisa mencatat message, nama error, dan stack trace ke log atau server backend.

4. Penting untuk aplikasi asynchronous

Request API bisa gagal kapan saja. Promise bisa error. async/await bisa melempar exception. Tanpa error handling, Anda akan buta terhadap situasi tersebut.

Contoh Kasus Aplikasi Tanpa Error Handling

Bayangkan Anda memiliki fungsi sederhana yang mengambil data user dari API:

async function getUser() {
        const response = await fetch("https://api.example.com/user");
        const data = await response.json();
        console.log("User:", data.name);
}

getUser();Code language: JavaScript (javascript)

Apa yang terjadi jika:

  • Koneksi internet lambat?
  • Endpoint API sedang down?
  • API mengembalikan struktur data berbeda?
  • Server memberikan status 500?

Aplikasi langsung melempar error dan berhenti.

Di console Anda mungkin akan melihat:

Uncaught (in promise) TypeError: Failed to fetchCode language: JavaScript (javascript)

Dan proses console.log tidak akan berjalan. Jika ini terjadi pada aplikasi yang digunakan pelanggan, mereka akan merasa aplikasi “rusak”, padahal hanya kurang error handling.

Manfaat Error Handling untuk Pengembangan Web Modern

Error handling bukan lagi opsional dalam pengembangan web modern. Ada beberapa manfaat besar:

  1. Aplikasi lebih stabil
    Dengan try...catch dan finally, setiap error dapat ditangani secara terkendali.

  2. Mudah di-maintain
    Developer dapat dengan cepat mengetahui sumber masalah dari stack trace yang ditangkap.

  3. User lebih percaya pada aplikasi
    Tidak ada yang suka aplikasi yang tiba-tiba tidak bekerja. Pesan error yang sopan jauh lebih baik daripada crash.

  4. Lebih aman
    Error yang tidak ditangani kadang membuka celah bagi attacker untuk melihat informasi internal aplikasi.

  5. Wajib untuk fitur besar
    Seperti:

    • API fetching
    • Authentication
    • Sistem pembayaran
    • Pengolahan file
    • Aplikasi realtime

Jadi, error handling adalah pondasi penting agar aplikasi tetap profesional, aman, dan nyaman digunakan.

Jenis-Jenis Error dalam JavaScript

Memahami jenis-jenis error di JavaScript sangat penting sebelum mempelajari cara menangani error dengan try…catch atau Promise. Setiap jenis error memiliki ciri dan penyebab yang berbeda. Dengan mengenal semuanya, proses debugging akan jauh lebih cepat dan efisien.

Syntax Error

Syntax Error terjadi ketika JavaScript tidak bisa memahami kode karena kesalahan penulisan. Ini seperti salah ketik yang membuat interpreter tidak bisa membaca instruksi.

Contoh kesalahan umum:

function sayHello() {
    console.log("Hello"
}Code language: JavaScript (javascript)

Error yang muncul:

Uncaught SyntaxError: Unexpected token '}'Code language: JavaScript (javascript)

Ciri-ciri Syntax Error:

  • Terjadi sebelum kode dijalankan.
  • Biasanya disebabkan oleh kurung yang kurang, tanda kutip tidak lengkap, atau karakter ilegal.
  • Dapat diketahui secara cepat oleh editor modern melalui highlight merah.

Syntax Error tidak bisa ditangani dengan try…catch, karena script tidak akan jalan sama sekali sebelum kesalahan diperbaiki.

Runtime Error

Runtime Error muncul saat kode sudah berjalan, tetapi ada sesuatu yang tidak valid.

Contoh:

let user = null;
console.log(user.name);Code language: JavaScript (javascript)

Error:

Uncaught TypeError: Cannot read properties of null (reading 'name')Code language: JavaScript (javascript)

Ciri-ciri:

  • Terjadi ketika program sedang berjalan.
  • Bisa ditangani dengan try…catch.
  • Sering disebabkan oleh nilai yang tidak sesuai ekspektasi.

Runtime Error adalah jenis error yang paling sering dihadapi developer sehari-hari.

Logical Error

Logical Error adalah kesalahan logika dalam algoritma sehingga aplikasi menghasilkan output yang salah. Ini tidak menghasilkan pesan error otomatis, tapi hasilnya tidak sesuai.

Contoh:

function tambah(a, b) {
    return a - b; // seharusnya a + b
}

console.log(tambah(5, 2)); // Output: 3 (salah)Code language: JavaScript (javascript)

Ciri-ciri:

  • Tidak ada pesan error.
  • Sangat sulit dideteksi.
  • Biasanya ditemukan melalui testing atau pengecekan manual.

Logical Error adalah “musuh tersembunyi” karena tidak terlihat di console.

TypeError, ReferenceError, RangeError, EvalError, URIError

JavaScript memiliki banyak jenis error bawaan (built-in errors) yang muncul saat terjadi kondisi tertentu. Berikut penjelasan yang paling penting:

1. TypeError

Muncul ketika tipe data atau operasi tidak sesuai.

Contoh:

let fn = 123;
fn(); // memanggil angka sebagai fungsiCode language: JavaScript (javascript)

Error:

Uncaught TypeError: fn is not a functionCode language: JavaScript (javascript)

2. ReferenceError

Terjadi saat variabel dipanggil tetapi tidak dideklarasikan.

console.log(name); // variabel tidak adaCode language: JavaScript (javascript)

Error:

Uncaught ReferenceError: name is not definedCode language: JavaScript (javascript)

3. RangeError

Biasanya muncul saat nilai berada di luar batas yang diperbolehkan, misalnya length array negatif.

let arr = new Array(-5);Code language: JavaScript (javascript)

Error:

Uncaught RangeError: Invalid array lengthCode language: JavaScript (javascript)

4. EvalError

Berkaitan dengan fungsi eval() (jarang muncul kecuali dalam implementasi lama).

5. URIError

Error yang muncul ketika fungsi decode/encode URI berhadapan dengan karakter ilegal.

decodeURIComponent("%");Code language: JavaScript (javascript)

Error:

Uncaught URIError: URI malformedCode language: JavaScript (javascript)

Cara Membaca Pesan Error di Console

Salah satu keahlian dasar developer adalah membaca error di DevTools.

Format umum error:

TypeError: Cannot read properties of undefined (reading 'length')
    at getUserName (app.js:12)
    at main (app.js:22)Code language: JavaScript (javascript)

Penjelasannya:

  • TypeError → jenis error
  • Cannot read properties… → pesan error
  • at getUserName (app.js:12) → lokasi error (file + baris)
  • at main (app.js:22) → fungsi yang memanggilnya

Tips membaca error:

  • Fokus pada baris pertama (jenis + pesan).
  • Cek baris file yang ditunjuk.
  • Lihat stack trace untuk mengetahui alur error.
  • Jika error terkait Promise, sering muncul label: Uncaught (in promise)

Error Stack Trace dan Artinya

Stack trace adalah daftar urutan fungsi yang dieksekusi hingga terjadi error. Ini sangat membantu debugging karena memperlihatkan jalan lengkap eksekusi program.

Contoh stack trace:

TypeError: Cannot read properties of undefined (reading 'length')
    at getUserName (app.js:12)
    at main (app.js:22)Code language: JavaScript (javascript)

Cara membacanya:

  • Baris pertama menunjukkan pesan error.
  • Baris selanjutnya menunjukkan jejak fungsi dari bawah ke atas.
  • Fungsi paling bawah di daftar adalah pemicu awal error.

Manfaat stack trace:

  • Mengetahui asal-usul error.
  • Memudahkan debugging.
  • Sangat penting dalam error handling pada Promise dan async/await.

Dasar Try, Catch, Finally

Struktur try…catch…finally adalah fondasi utama dalam error handling JavaScript. Dengan memahami bagian-bagiannya, Anda dapat mencegah aplikasi crash, memberikan pesan error yang lebih jelas, dan menjaga proses penting tetap berjalan.

Cara Kerja Try

Bagian try adalah tempat Anda menulis kode yang berpotensi menghasilkan error. JavaScript akan menjalankan kode dalam blok try seperti biasa, tetapi jika terjadi kesalahan, eksekusi akan langsung dialihkan ke catch.

Contoh sederhana:

try {
    let data = JSON.parse("ini bukan JSON");
    console.log(data);
}Code language: JavaScript (javascript)

Apa yang terjadi?

  • JavaScript mencoba menjalankan kode.
  • Saat mencapai JSON.parse, terjadi error.
  • Eksekusi langsung dihentikan.
  • Baris setelah error tidak dijalankan.

Program melompat ke blok catch.

Tujuan try:

  • Mendeteksi potensi error.
  • Mengelompokkan blok kode kritis.
  • Mencegah aplikasi berhenti total.

Fungsi Catch

Blok catch digunakan untuk menangani error yang terjadi dalam try. Di dalamnya, Anda dapat:

  • Menampilkan pesan error
  • Menyimpan error ke log server
  • Menampilkan notifikasi ke user
  • Melakukan recovery (fallback)

Contoh:

try {
    let result = number.toUpperCase(); // number tidak didefinisikan
} catch (error) {
    console.error("Terjadi error:", error.message);
}Code language: JavaScript (javascript)

Output:

Terjadi error: number is not defined

catch menerima satu parameter, biasa diberi nama:

  • error
  • err
  • e

Objek error biasanya memiliki:

  • name → jenis error (TypeError, ReferenceError, dll)
  • message → deskripsi error
  • stack → jejak fungsi penyebab error

Yang membuat catch sangat penting:

  • Anda dapat menangani error tanpa menghentikan program.
  • Cocok untuk debugging dan logging.
  • Bisa digunakan untuk memberi fallback, misalnya:
let username;

try {
    username = getUserName();
} catch (e) {
    username = "Guest"; // fallback
}Code language: JavaScript (javascript)

Kapan Finally Dijalankan

finally adalah blok yang selalu dijalankan, terlepas dari apakah terjadi error atau tidak. Ini berguna untuk:

  • Membersihkan resource (misal: koneksi database)
  • Menghentikan loading spinner
  • Menutup modal
  • Mengembalikan state aplikasi

Contoh:

try {
    console.log("Memproses data...");
    throw new Error("Gagal memproses");
} catch (err) {
    console.log("Error:", err.message);
} finally {
    console.log("Selesai. Membersihkan...");
}Code language: JavaScript (javascript)

Output:

Memproses data...
Error: Gagal memproses
Selesai. Membersihkan...

finally tetap dijalankan meskipun:

  • Ada error
  • Tidak ada error
  • Ada return di dalam try
  • Ada return di dalam catch

Bahkan:

Perilaku Try/Catch pada Browser vs Node.js

Secara konsep, try/catch sama di keduanya, namun ada beberapa perbedaan kecil akibat lingkungan eksekusi.

  1. Browser

    • Error sering muncul akibat DOM, event handler, atau API browser.
    • Error pada asynchronous (Promise) tidak tertangkap otomatis oleh try/catch tanpa await.
    • Console browser menampilkan error dengan link file dan nomor baris.
  2. Node.js

    • Sering berkaitan dengan modul, file system, dan proses server.
    • Error fatal dapat menghentikan proses Node.
    • Menawarkan fitur tambahan seperti process.on(‘uncaughtException’).

Contoh di Node.js:

try {
    require("file-yang-tidak-ada");
} catch (e) {
    console.error("Error module:", e.message);
}Code language: JavaScript (javascript)

Contoh Kasus Simple

Contoh praktis error handling untuk pemula:

Kasus: Membaca input user yang tidak valid

function hitungDiskon(harga) {
    try {
        if (typeof harga !== "number") {
            throw new Error("Harga harus berupa angka");
        }

        let diskon = harga * 0.2;

        return diskon;

    } catch (error) {
        console.error("Error:", error.message);
        return 0; // fallback
    } finally {
        console.log("Fungsi hitungDiskon selesai dijalankan.");
    }
}

console.log(hitungDiskon(100000)); // 20000
console.log(hitungDiskon("seratus ribu")); // Error → 0Code language: JavaScript (javascript)

Penjelasan:

  • Error ditangkap di catch.
  • Program tetap berjalan normal.
  • finally tetap dijalankan, bahkan jika input tidak valid.

Throw: Membuat Error Sendiri

Selain menangani error, JavaScript memungkinkan kita membuat error secara manual menggunakan throw. Fitur ini penting untuk membuat validasi yang lebih kuat, menjaga kualitas data, dan mengontrol alur program dengan baik. Dengan throw, kita bisa menentukan kapan sebuah kondisi dianggap bermasalah dan harus dihentikan.

Kapan Menggunakan throw

Gunakan throw ketika Anda ingin:

1. Menghentikan eksekusi secara terkontrol

Jika ada kondisi yang tidak valid, Anda bisa segera menghentikan fungsi tanpa menunggu error alami muncul.

Contoh:

function bagi(a, b) {
    if (b === 0) {
        throw "Pembagian dengan nol tidak diperbolehkan!";
    }
    return a / b;
}

console.log(bagi(10, 2)); // 5
console.log(bagi(10, 0)); // ErrorCode language: JavaScript (javascript)

2. Memberikan pesan error yang lebih jelas

Error default JavaScript kadang tidak informatif. Dengan throw, Anda bisa membuat error yang mudah dipahami.

3. Melakukan validasi input

Validasi sangat umum dalam aplikasi web, seperti memeriksa email, nomor telepon, atau data form.

4. Mengontrol alur Promise dan async/await

Dalam async function, throw otomatis menghasilkan rejected Promise.

Contoh:

async function getUser() {
    throw new Error("Token tidak valid");
}Code language: JavaScript (javascript)

5. Menghasilkan error yang bisa di-catch

Anda dapat menangkap error yang dilempar menggunakan try…catch.

throw vs throw new Error

Banyak pemula bingung: harus pakai throw "pesan" atau throw new Error("pesan")?

Keduanya bisa, tapi sangat berbeda.

throw “pesan”

Anda melempar nilai string biasa, bukan objek Error.

Contoh:

throw "Terjadi kesalahan";Code language: JavaScript (javascript)

Kekurangannya:

  • Tidak memiliki name
  • Tidak memiliki stack trace
  • Sulit dideteksi dalam logging
  • Tidak standar

throw new Error(“pesan”)

Cara yang direkomendasikan.

throw new Error("Data user tidak ditemukan");Code language: JavaScript (javascript)

Keuntungannya:

  • Memiliki properti lengkap (name, message, stack)
  • Mudah dilacak di console
  • Bisa dibuat custom kelas error sendiri
  • Sesuai standar industri

Perbandingan singkat

TeknikDianjurkan?Alasan
throw "pesan"TidakTidak ada detail error, sulit dilacak
throw new Error("pesan")YaLebih rapi, lengkap, standar
throw new CustomError(...)Sangat direkomendasikanUntuk sistem error profesional

Membuat Custom Validation Error

Anda bisa membuat kelas error sendiri dengan mewarisi dari Error. Ini sangat bermanfaat ketika Anda ingin membedakan berbagai jenis error (validasi, otorisasi, database, dll).

Contoh membuat class custom:

class ValidationError extends Error {
    constructor(message) {
        super(message);
        this.name = "ValidationError";
    }
}

function daftarUser(username) {
    if (username.length < 3) {
        throw new ValidationError("Username minimal 3 karakter!");
    }
    return "Pendaftaran berhasil";
}

try {
    daftarUser("ab");
} catch (error) {
    if (error instanceof ValidationError) {
        console.error("Validasi gagal:", error.message);
    } else {
        console.error("Error lain:", error.message);
    }
}Code language: JavaScript (javascript)

Keuntungan memakai custom error:

  • Memudahkan debugging
    Anda tahu persis jenis error yang muncul.

  • Bisa membuat kategori error
    Misalnya:

    • ValidationError
    • AuthenticationError
    • DatabaseError
    • NetworkError
  • Profesional dan siap production
    Aplikasi besar seperti React, Vue, Node.js semuanya memakai custom error.

Praktik Terbaik dalam Pembuatan Custom Error

Untuk menjaga kualitas codebase, berikut best practice yang dianjurkan:

1. Selalu gunakan kelas turunan dari Error

Bukan string atau object biasa.

class AppError extends Error {}Code language: JavaScript (javascript)

2. Tambahkan properti tambahan jika perlu

Misalnya statusCode untuk API.

class APIError extends Error {
    constructor(message, statusCode) {
        super(message);
        this.statusCode = statusCode;
        this.name = "APIError";
    }
}Code language: JavaScript (javascript)

3. Gunakan nama error yang konsisten

Nama format yang umum:

  • ValidationError
  • AuthError
  • NotFoundError

4. Jangan gunakan custom error untuk hal kecil

Misal:

❌ Tidak perlu membuat error khusus hanya untuk mengecek angka negatif.

Custom error perlu dipakai untuk kondisi besar dan penting.

5. Selalu sertakan pesan error yang jelas

Contoh buruk:

throw new Error("Error");Code language: JavaScript (javascript)

Contoh baik:

throw new Error("Username tidak boleh kosong");Code language: JavaScript (javascript)

6. Jangan lupa panggil super(message)

Jika tidak, properti message tidak akan terbentuk dengan benar.

7. Gunakan instanceof untuk identifikasi

Ini cara terbaik memisahkan jenis error.

if (error instanceof ValidationError) {
    // handle validasi
}Code language: JavaScript (javascript)

Error Handling pada Fungsi dan Modular Code

Dalam aplikasi JavaScript modern yang sudah modular dan terdiri dari banyak fungsi, tantangan utama adalah menempatkan error handling pada lokasi yang tepat. Kesalahan menaruh try…catch dapat menyebabkan kode berantakan, error tidak muncul, atau malah menimbulkan silent bug yang sulit dideteksi.

Bagian ini membahas bagaimana menangani error di level fungsi, modul, dan level global.

Menempatkan Error Handling di Level yang Tepat

Kesalahan terbesar pemula adalah:

  • Meletakkan try…catch terlalu banyak
  • Meletakkan try…catch terlalu dekat ke setiap baris kode
  • Meletakkan error handling di lokasi yang tidak strategis
  • Atau sebaliknya: tidak memasang error handling sama sekali

Prinsip utama:

  • Letakkan error handling sedekat mungkin dengan logika bisnis yang Anda ingin lindungi.

Contoh yang Salah (Terlalu banyak try/catch)

function prosesData(data) {
    try {
        let x = data.x;
    } catch (e) {}

    try {
        let y = data.y;
    } catch (e) {}

    try {
        return x + y;
    } catch (e) {}
}Code language: JavaScript (javascript)

Ini membuat debugging menjadi mimpi buruk, karena error “ditelan” di setiap tahap.

Contoh yang Benar (Satu try/catch untuk satu unit logika)

function prosesData(data) {
    try {
        const { x, y } = data;
        return x + y;
    } catch (error) {
        console.error("Gagal memproses data:", error.message);
        return null;
    }
}Code language: JavaScript (javascript)

Best practice penentuan lokasi try/catch:

  • Taruh di batas unit of work (fungsi, handler event, atau modul).
  • Jangan gunakan try…catch untuk validasi kecil—gunakan if.
  • Gunakan try…catch hanya untuk kode yang berpotensi melempar error.
  • Hindari menangkap error terlalu awal; biarkan naik jika perlu.

Menangkap Error di Dalam Fungsi

Fungsi adalah tempat paling strategis untuk menangkap error tertentu. Misal:

  • Validasi input
  • Panggilan API internal
  • Konversi data
  • Proses kalkulasi penting

Contoh:

function parseUser(jsonText) {
    try {
        const user = JSON.parse(jsonText);
        return user;
    } catch (error) {
        throw new Error("Format JSON tidak valid");
    }
}Code language: JavaScript (javascript)

Di sini fungsi parseUser memutuskan:

  • Error JSON asli tidak diteruskan apa adanya
  • Error dikonversi menjadi pesan yang lebih jelas
  • Error dilempar lagi ke level atas

Kapan menangkap error di dalam fungsi?

Gunakan teknik ini jika:

  • Fungsi perlu memberi error yang lebih spesifik
  • Anda ingin membungkus error eksternal menjadi error internal
  • Anda ingin memastikan fungsi tetap pure atau rapi
  • Fungsi dijalankan berkali-kali dan Anda ingin pesan konsisten

Meneruskan Error ke Atas (Error Propagation)

Terkadang, sebuah fungsi tidak boleh menangani error di dalamnya.

Biarkan error naik ke fungsi pemanggil (throw upward) agar dapat ditangani di level lebih tinggi.

Contoh:

function hitungTotal(items) {
    if (!Array.isArray(items)) {
        throw new Error("items harus berupa array");
    }

    return items.reduce((a, b) => a + b);
}

function main() {
    try {
        const total = hitungTotal("bukan array");
        console.log("Total:", total);
    } catch (error) {
        console.error("Terjadi kesalahan:", error.message);
    }
}

main();Code language: JavaScript (javascript)

Di sini:

  • hitungTotal() tidak menangkap error (SENGAJA)
  • Errornya diteruskan ke main()
  • main() yang menangani error secara global di proses itu

Kapan Anda harus melakukan error propagation?

  • Fungsi adalah bagian dari sistem yang lebih besar
  • Anda ingin error ditangani di suatu lapisan (misal “controller”, bukan “service”)
  • Modul kecil tidak perlu tahu cara menangani error
  • Menggunakan arsitektur seperti MVC, Clean Architecture, atau modul API

Error Handling Global (window.onerror / process.on)

Selain menangani error di level fungsi dan modul, JavaScript menyediakan error handler global untuk menangkap error yang tidak tertangkap (uncaught errors).

1. Global Error Handling di Browser

Browser menyediakan:

window.onerror

window.onerror = function (message, source, lineno, colno, error) {
        console.log("Global Error:", message);
        console.log("Lokasi:", source + ":" + lineno);
        console.log("Detail:", error);
};Code language: JavaScript (javascript)

Fungsi ini menangkap:

  • Error runtime yang tidak di-catch
  • Error syntax (kadang)
  • Error dari event handler

window.addEventListener("unhandledrejection")

Menangani Promise yang gagal tanpa .catch:

window.addEventListener("unhandledrejection", (event) => {
    console.error("Unhandled Promise:", event.reason);
});Code language: JavaScript (javascript)

2. Global Error Handling di Node.js

Node.js memberikan dua event khusus:

process.on("uncaughtException")

Menangkap error synchronous yang tidak tertangkap.

process.on("uncaughtException", (error) => {
    console.error("Uncaught Exception:", error);
    // idealnya lakukan restart server
});Code language: JavaScript (javascript)

process.on("unhandledRejection")

Untuk Promise tanpa .catch:

process.on("unhandledRejection", (reason) => {
    console.error("Unhandled Rejection:", reason);
});Code language: JavaScript (javascript)

Catatan Penting:

Menggunakan error handler global tidak menggantikan try/catch lokal, tetapi:

  • Menjadi laporan terakhir sebelum aplikasi mati
  • Berguna untuk logging server (Sentry, LogRocket, ELK)
  • Tidak disarankan untuk recovery logika bisnis
  • Lebih cocok untuk notifikasi dan debugging

Error Handling pada Asynchronous JavaScript

Asynchronous JavaScript menghadirkan tantangan tersendiri dalam error handling. Berbeda dengan kode sinkron yang mudah di-trace, error pada async code sering muncul terlambat, di luar blok eksekusi utama, atau tersembunyi dalam callback atau promise chain. Bagian ini membahas seluruh teknik modern untuk menangani error dalam kode asynchronous.

Tantangan Error Handling pada Async Code

Beberapa masalah umum dalam asynchronous JavaScript:

1. Error Tidak Terjadi di Tempat yang Sama dengan Kode Pemanggil

Contoh: callback dipanggil beberapa detik kemudian, sehingga try/catch awal tidak mempengaruhi error yang terjadi.

2. Callback Hell Memperburuk Trace Error

Semakin dalam callback bersarang, semakin sulit membaca alur kesalahan.

3. Promise Membutuhkan Catch Tersendiri

Error hanya tertangkap jika chain memiliki .catch().

4. Async/Await Butuh try/catch di Tiap Level

Jika tidak menggunakan try/catch, error dilempar sebagai rejected promise.

5. Silent Error

Misalnya, Promise yang tidak ditangkap akan menimbulkan “UnhandledPromiseRejection”.

Penanganan Error pada Callback

Callback menggunakan pola error-first:

function getData(callback) {
    setTimeout(() => {
        const error = Math.random() > 0.5 ? "Gagal ambil data" : null
        if (error) return callback(error)
        callback(null, { name: "John" })
    }, 1000)
}

getData((err, data) => {
    if (err) {
        console.error("Error:", err)
        return
    }
    console.log("Data:", data)
})Code language: JavaScript (javascript)

Ciri khas pola ini:

  • Parameter pertama = error
  • Callback harus selalu memeriksa error
  • Rawat error sedini mungkin untuk menghindari callback hell

Penanganan Error pada Promise (catch)

Error pada promise tertangkap menggunakan .catch().

Contoh:

const getUser = () => {
    return new Promise((resolve, reject) => {
        reject("User tidak ditemukan")
    })
}

getUser()
    .then(user => console.log(user))
    .catch(err => console.error("Error:", err))Code language: JavaScript (javascript)

Behavior penting:

  • Error dalam then otomatis masuk ke catch
Promise.resolve(10)
    .then(() => {
        throw "Oops"
    })
    .catch(err => console.log("Tertangkap:", err))Code language: JavaScript (javascript)

Promise.all dan Error yang Menghentikan Semua

Promise.all() akan langsung reject jika salah satu promise gagal.

Promise.all([
    Promise.resolve("A"),
    Promise.reject("Error pada B"),
    Promise.resolve("C")
])
.then(result => console.log(result))
.catch(err => console.error("Error:", err))Code language: JavaScript (javascript)

Output:

Error: Error pada BCode language: JavaScript (javascript)

Solusi: Gunakan Promise.allSettled() jika ingin menangkap semua hasil, termasuk error.

Promise.allSettled([
    Promise.resolve("A"),
    Promise.reject("Error B"),
    Promise.resolve("C")
]).then(console.log)Code language: JavaScript (javascript)

Output:

[
    { status: "fulfilled", value: "A" },
    { status: "rejected", reason: "Error B" },
    { status: "fulfilled", value: "C" }
]Code language: JavaScript (javascript)

Ini sangat berguna untuk tasks yang independen.

Error Handling pada Async/Await dengan try/catch

Async/await membuat penulisan lebih rapi, tetapi error harus ditangani dengan try/catch:

async function getData() {
    try {
        const result = await fetch("https://api.example.com/data")
        return await result.json()
    } catch (err) {
        console.error("Terjadi error:", err)
    }
}Code language: JavaScript (javascript)

Error di dalam async function → rejected promise

Jika function async tidak punya try/catch:

async function test() {
    throw "Oops"
}Code language: JavaScript (javascript)

test() menghasilkan rejected promise.

Contoh Kasus API Fetch (Async/Await)

Contoh lengkap:

async function getUser() {
    try {
        const response = await fetch("https://jsonplaceholder.typicode.com/users/1")

        if (!response.ok) {
            throw new Error("HTTP Error, status: " + response.status)
        }

        const data = await response.json()
        console.log("User:", data)

    } catch (err) {
        console.error("Gagal mengambil user:", err.message)
    }
}

getUser()Code language: JavaScript (javascript)

Error yang dapat terjadi:

  • Jaringan mati
  • Server down
  • Response tidak valid JSON
  • Status HTTP 4xx/5xx
  • Timeout (butuh AbortController)

Kesalahan Umum yang Perlu Dihindari

  1. Tidak memberi .catch() pada promise
    Dapat menyebabkan error global “UnhandledPromiseRejection”.

  2. Menggunakan try/catch pada kode async callback
    Try/catch tidak bekerja pada kode async yang tidak di-await.

  3. Tidak memvalidasi response HTTP
    fetch() tidak melempar error untuk status 404/500.

  4. Mengabaikan return dalam async
    Menghasilkan promise yang tidak pernah resolve.

  5. Menggabungkan callback + promise tanpa alasan
    Berpotensi membuat “callback-promise hell”.

  6. Tidak menempatkan error handling pada level yang tepat
    Harus ada error handling:

    • di tiap async function
    • di level global
    • di level UI (user feedback)

Debugging Error di JavaScript

Debugging adalah proses menemukan sumber masalah dalam kode. Dalam JavaScript, kemampuan debugging yang baik akan mempercepat penyelesaian error, meningkatkan kualitas aplikasi, dan mengurangi risiko bug berulang. Bagian ini membahas teknik dasar debugging modern, mulai dari membaca stack trace hingga mengoptimalkan penggunaan DevTools.

Membaca Stack Trace dengan Benar

Stack trace adalah daftar urutan fungsi yang dipanggil sebelum error terjadi. Ini sangat penting untuk menemukan lokasi sebenarnya dari error.

Contoh error:

Uncaught TypeError: Cannot read properties of undefined (reading 'name')
    at getUserName (script.js:15)
    at main (script.js:28)
    at script.js:32Code language: JavaScript (javascript)

Cara membacanya:

  1. Baris pertama: jenis error

    • TypeError: Cannot read properties of undefined
  2. Baris berikutnya: lokasi fungsi penyebab error

    • at getUserName (script.js:15)
      Ini adalah sumber error utama yang harus dicek dulu.
  3. Fungsi di atasnya: siapa yang memanggil fungsi error

    • at main (script.js:28)
  4. Baris terakhir: titik awal pemanggilan

    • script.js:32 adalah eksekusi awal yang memanggil main()

Console.log, Console.error, Console.warn

Console sangat membantu dalam debugging.

1. console.log()

Digunakan untuk melihat nilai variabel atau proses.

console.log("User:", user)Code language: JavaScript (javascript)

2. console.error()

Menampilkan error dengan styling merah → mudah terlihat.

console.error("Gagal load:", err)Code language: JavaScript (javascript)

3. console.warn()

Untuk menampilkan peringatan.

console.warn("Data tidak lengkap")Code language: JavaScript (javascript)

Tambahan penting:

  • console.table() → menampilkan array/object dalam bentuk tabel.
  • console.time() dan console.timeEnd() → memeriksa performa.
  • console.group() → mengelompokkan log agar rapi.

Menggunakan Debugger (Chrome DevTools)

DevTools memiliki fitur debugger yang menghentikan eksekusi kode secara otomatis.

Cara 1 — Menulis keyword debugger di kode:

function calculate() {
    debugger
    let x = 10
    let y = 20
    return x + y
}Code language: JavaScript (javascript)

Saat halaman dijalankan, DevTools akan berhenti di baris debugger.

Cara 2 — Membuka Sources Tab di Chrome:

  1. Buka DevTools → tab Sources
  2. Cari file JavaScript
  3. Klik nomor baris untuk membuat breakpoint
  4. Reload halaman
  5. Eksekusi akan berhenti tepat di baris tersebut

Keuntungan:

  • Melihat nilai variabel secara real-time
  • Mengontrol alur program
  • Melihat call stack dengan jelas
  • Bisa mengubah nilai variabel pada saat pause

Breakpoints, Step Over, Step Into

Breakpoints

Titik berhenti eksekusi kode. Digunakan untuk memeriksa keadaan aplikasi sebelum error.

Jenis breakpoints:

  • Line Breakpoint — berhenti pada baris tertentu
  • Conditional Breakpoint — berhenti hanya jika kondisi terpenuhi
    Contoh: klik kanan nomor baris → “Add conditional breakpoint”
    Kondisi: user.id === 5

Step Over (F10)

Menjalankan baris kode tanpa masuk ke fungsi yang dipanggil.

Cocok untuk:

  • Menghindari fungsi besar yang tidak relevan

Step Into (F11)

Masuk ke dalam fungsi yang dipanggil pada baris tersebut.

Cocok untuk:

  • Menelusuri error di fungsi internal

Step Out (Shift + F11)

Keluar dari fungsi saat ini dan kembali ke pemanggilnya.

Kapan digunakan:

  • Jika Anda masuk terlalu dalam dan ingin kembali ke alur utama

Tips Debugging untuk Pemula

1. Jangan langsung memperbaiki error—baca errornya dulu

Error JavaScript biasanya memberikan petunjuk lengkap:

  • Jenis error
  • Baris
  • File
  • Stack trace

2. Reproduksi error secara konsisten

Error yang bisa diulang → lebih mudah diperbaiki.

3. Gunakan console.log() secara strategis

Debugging bukan spam log:

  • Log input fungsi
  • Log output fungsi
  • Log nilai yang mencurigakan

4. Periksa apakah error disebabkan data kosong

80% error terjadi karena:

  • null
  • undefined
  • NaN
  • string kosong
  • array kosong

5. Isolasi bug

Pindahkan bagian kode ke area terpisah untuk mengetahui apakah error datang dari:

  • logika function
  • data
  • state UI

6. Gunakan “Pretty Print” untuk file minified

Jika error berasal dari file .min.js, klik tombol { } di DevTools.

7. Gunakan breakpoints daripada spam console.log

Breakpoints memberikan:

  • Nilai live variable
  • Arah alur eksekusi
  • Call stack penuh

8. Cek environment

Kadang error muncul karena:

  • Cache browser
  • Response API tidak sesuai
  • Perbedaan Node.js version

9. Coba jalankan di environment lain

Jika error hanya muncul di satu browser:

  • Cek compatibility
  • Cek polyfill

10. Periksa dokumentasi fungsi/library

Kadang error muncul karena salah format data atau aturan library.

Custom Error di JavaScript

Selain error bawaan JavaScript seperti TypeError, ReferenceError, dan RangeError, aplikasi modern sering membutuhkan custom error. Custom error memungkinkan developer:

  • Memberikan pesan error yang lebih spesifik
  • Menandai jenis error dengan jelas
  • Menangani error berbeda dengan cara berbeda di aplikasi besar

Kenapa Membuat Custom Error

Alasan membuat custom error:

  1. Klasifikasi error lebih jelas
    Misal membedakan error validasi dari error server.

  2. Memudahkan debugging
    Memudahkan tim untuk memahami error tanpa membaca message panjang.

  3. Mendukung arsitektur modular
    Setiap modul dapat melempar jenis error spesifik.

  4. Memberikan info tambahan
    Misal kode status HTTP, ID transaksi, atau level error.

Membuat Class Error Sendiri

Cara membuat custom error:

class ValidationError extends Error {
    constructor(message) {
        super(message);
        this.name = "ValidationError";
    }
}Code language: JavaScript (javascript)

Penjelasan:

  • extends Error → mewarisi properti error standar
  • super(message) → memastikan message error tersimpan
  • this.name → menandai jenis error

Menyimpan Informasi Tambahan

Custom error bisa menyimpan data tambahan untuk membantu debugging atau reporting:

class APIError extends Error {
    constructor(message, statusCode, endpoint) {
        super(message);
        this.name = "APIError";
        this.statusCode = statusCode;
        this.endpoint = endpoint;
    }
}

const error = new APIError("Tidak dapat mengambil data", 500, "/users");
console.log(error.statusCode); // 500
console.log(error.endpoint);   // /usersCode language: JavaScript (javascript)

Menangani Custom Error di Aplikasi Besar

Di aplikasi besar, penting untuk menangkap dan memproses custom error secara berbeda.

Contoh:

try {
    throw new ValidationError("Nama wajib diisi");
} catch (err) {
    if (err instanceof ValidationError) {
        console.warn("Validasi gagal:", err.message);
    } else if (err instanceof APIError) {
        console.error("API Error:", err.message, err.statusCode);
    } else {
        console.error("Error lain:", err.message);
    }
}Code language: JavaScript (javascript)

Keuntungan:

  • Pesan error lebih spesifik untuk user dan developer
  • Bisa memisahkan error log antara validasi, server, atau runtime

Contoh Penggunaan Custom Error dalam Validasi Input

Praktik umum custom error: validasi input form.

class InputError extends Error {
    constructor(field, message) {
        super(message);
        this.name = "InputError";
        this.field = field;
    }
}

function validateUser(user) {
    if (!user.name) {
        throw new InputError("name", "Nama tidak boleh kosong");
    }
    if (!user.email.includes("@")) {
        throw new InputError("email", "Email tidak valid");
    }
    return true;
}

try {
    validateUser({ name: "", email: "arisexample.com" });
} catch (err) {
    if (err instanceof InputError) {
        console.error(`Error pada field ${err.field}: ${err.message}`);
    }
}Code language: JavaScript (javascript)

Output:

Error pada field name: Nama tidak boleh kosongCode language: JavaScript (javascript)

Manfaat:

  • Mudah mengaitkan error dengan field input
  • Memberikan pesan yang jelas ke UI
  • Memudahkan logging error di backend

Kesimpulan

Error handling di JavaScript adalah bagian penting untuk menulis kode yang aman, stabil, dan mudah dipelihara. Dengan memahami try…catch…finally, throw, serta custom error, developer dapat menangani berbagai jenis error, mulai dari syntax, runtime, hingga logical error, baik pada kode sinkron maupun asynchronous. Selain itu, penerapan error handling yang tepat memudahkan debugging, meminimalkan crash aplikasi, dan memberikan pengalaman pengguna yang lebih baik.

Setelah menguasai dasar-dasar ini, langkah selanjutnya adalah mengeksplorasi topik lanjutan seperti Promise, async/await, Fetch API, dan Observables. Memahami alur error pada asynchronous code dan teknik modern dalam menangani error akan membuat aplikasi web lebih robust, modular, dan siap untuk pengembangan skala besar.

1 JavaScript Dasar

2 JavaScript Menengah

Level Menengah fokus pada kemampuan yang lebih luas, seperti manipulasi DOM, event JavaScript, form validation, JSON, LocalStorage, SessionStorage, hingga asynchronous JavaScript dasar. Level ini membantu pengguna memahami bagaimana JavaScript bekerja untuk membuat website lebih interaktif dan dinamis, sangat relevan untuk tutorial dasar yang mengarahkan ke kemampuan membuat fitur web praktis.

3 JavaScript Lanjutan

4 JavaScript Mahir

5 JavaScript Ahli

Comments

Congrats, you have the opportunity to be the first commenter on this article. Have questions or suggestions? Please leave a comment to start discussion.

Leave comment

Alamat email Anda tidak akan dipublikasikan. Required fields are marked *