JavaScript Error Handling: try/catch ve Custom Error'lar

JavaScript Error Handling: try/catch ve Custom Error'lar
Hata yönetimi, güvenilir ve sağlam uygulamalar geliştirmenin temel taşıdır. JavaScript'te try/catch/finally bloğu, Error sınıfı hiyerarşisi, custom error türleri ve asenkron hata yönetimi pattern'leri ile uygulamalarınızı beklenmedik durumlara karşı koruyabilirsiniz. Bu yazıda hata yönetiminin tüm yönlerini kapsamlı örneklerle inceliyoruz.
1. try/catch/finally Temelleri
try bloğu hata oluşabilecek kodu sarar, catch hatayı yakalar ve finally her durumda çalışır.
// Temel hata yakalama
try {
const veri = JSON.parse('{ geçersiz json }');
} catch (hata) {
console.error("JSON parse hatası:", hata.message);
// "JSON parse hatası: Expected property name or '}' ..."
} finally {
console.log("Bu blok her zaman çalışır");
}
// Hata türüne göre işleme
try {
bilinmeyenFonksiyon();
} catch (hata) {
if (hata instanceof ReferenceError) {
console.error("Tanımsız değişken/fonksiyon:", hata.message);
} else if (hata instanceof TypeError) {
console.error("Tür hatası:", hata.message);
} else if (hata instanceof SyntaxError) {
console.error("Sözdizimi hatası:", hata.message);
} else {
console.error("Bilinmeyen hata:", hata);
throw hata; // Bilinmeyen hataları yeniden fırlat
}
}
// finally ile kaynak temizleme
function dosyaIsle(dosyaYolu) {
let dosya = null;
try {
dosya = dosyaAc(dosyaYolu);
const icerik = dosya.oku();
return isleIcerik(icerik);
} catch (hata) {
console.error("Dosya işleme hatası:", hata.message);
return null;
} finally {
// Hata olsun olmasın dosyayı kapat
if (dosya) dosya.kapat();
}
}
2. Custom Error Sınıfları
Yerleşik Error sınıfını genişleterek uygulamanıza özgü hata türleri oluşturabilirsiniz. Bu, hata türüne göre farklı davranışlar sergilemenizi kolaylaştırır.
// Temel custom error
class UygulamaHatasi extends Error {
constructor(mesaj, kod) {
super(mesaj);
this.name = "UygulamaHatasi";
this.kod = kod;
this.tarih = new Date();
}
}
// Spesifik hata türleri
class DogrulamaHatasi extends UygulamaHatasi {
constructor(alanlar) {
super("Doğrulama hatası", "VALIDATION_ERROR");
this.name = "DogrulamaHatasi";
this.alanlar = alanlar;
}
}
class YetkilendirmeHatasi extends UygulamaHatasi {
constructor(mesaj = "Yetkisiz erişim") {
super(mesaj, "AUTH_ERROR");
this.name = "YetkilendirmeHatasi";
this.statusCode = 401;
}
}
class BulunamadiHatasi extends UygulamaHatasi {
constructor(kaynak, id) {
super(`${kaynak} bulunamadı: ${id}`, "NOT_FOUND");
this.name = "BulunamadiHatasi";
this.statusCode = 404;
this.kaynak = kaynak;
this.kaynakId = id;
}
}
// Kullanım
function kullaniciGetir(id) {
const kullanici = veritabani.bul(id);
if (!kullanici) {
throw new BulunamadiHatasi("Kullanici", id);
}
return kullanici;
}
try {
const kullanici = kullaniciGetir(999);
} catch (hata) {
if (hata instanceof BulunamadiHatasi) {
console.log(`${hata.kaynak} #${hata.kaynakId} bulunamadı`);
} else if (hata instanceof YetkilendirmeHatasi) {
console.log("Lütfen giriş yapın");
}
}
this.name özelliğini sınıf adı ile eşleştirin. Bu, hata mesajlarında ve loglarda hata türünün kolayca tanınmasını sağlar. Error.captureStackTrace (V8) ile stack trace'i de temizleyebilirsiniz.
3. Asenkron Hata Yönetimi
Asenkron kodda hata yönetimi, senkron koddan farklı yaklaşımlar gerektirir. Promise rejection'ları ve async/await hataları için farklı stratejiler vardır.
// async/await ile hata yönetimi
async function apiIstegi(endpoint) {
try {
const yanit = await fetch(endpoint);
if (!yanit.ok) {
const hataDetay = await yanit.json().catch(() => ({}));
throw new UygulamaHatasi(
hataDetay.mesaj || `HTTP Hatası: ${yanit.status}`,
`HTTP_${yanit.status}`
);
}
return await yanit.json();
} catch (hata) {
if (hata instanceof UygulamaHatasi) throw hata;
// Ağ hatası
if (hata.name === "TypeError" && hata.message.includes("fetch")) {
throw new UygulamaHatasi("Ağ bağlantısı yok", "NETWORK_ERROR");
}
throw hata;
}
}
// Global unhandled rejection yakalama
window.addEventListener("unhandledrejection", (event) => {
console.error("Yakalanmamış Promise hatası:", event.reason);
event.preventDefault(); // Varsayılan konsol hatasını engelle
hataRaporla(event.reason);
});
// Global error yakalama
window.addEventListener("error", (event) => {
console.error("Yakalanmamış hata:", event.error);
hataRaporla(event.error);
});
// Retry pattern
async function yenidenDene(fn, { maxDeneme = 3, gecikme = 1000 } = {}) {
for (let deneme = 1; deneme <= maxDeneme; deneme++) {
try {
return await fn();
} catch (hata) {
if (deneme === maxDeneme) throw hata;
console.warn(`Deneme ${deneme} başarısız, ${gecikme}ms sonra tekrar...`);
await new Promise(r => setTimeout(r, gecikme * deneme));
}
}
}
const veri = await yenidenDene(() => apiIstegi("/api/data"), {
maxDeneme: 3,
gecikme: 2000
});
4. Error Boundary Pattern'i
// Fonksiyonel error boundary
function guvenliCalistir(fn, varsayilan = null) {
try {
const sonuc = fn();
// Promise ise asenkron hatayı da yakala
if (sonuc instanceof Promise) {
return sonuc.catch(hata => {
console.error("Asenkron hata:", hata);
return varsayilan;
});
}
return sonuc;
} catch (hata) {
console.error("Senkron hata:", hata);
return varsayilan;
}
}
const sonuc = guvenliCalistir(() => riskliIslem(), "varsayılan");
// Merkezi hata yönetimi
class HataYoneticisi {
#isleyiciler = new Map();
kaydet(HataSinifi, isleyici) {
this.#isleyiciler.set(HataSinifi, isleyici);
return this;
}
isle(hata) {
for (const [Sinif, isleyici] of this.#isleyiciler) {
if (hata instanceof Sinif) {
return isleyici(hata);
}
}
// Varsayılan işleyici
console.error("İşlenemeyen hata:", hata);
throw hata;
}
}
const hataYoneticisi = new HataYoneticisi()
.kaydet(DogrulamaHatasi, (h) => ({ status: 400, alanlar: h.alanlar }))
.kaydet(YetkilendirmeHatasi, (h) => ({ status: 401, mesaj: h.message }))
.kaydet(BulunamadiHatasi, (h) => ({ status: 404, mesaj: h.message }));
catch(e) {}). En azından loglayın. Yakalanmamış hatalar için global handler'lar ekleyin ve production ortamında hata raporlama servisi kullanın.
Error.isError() metodu, bir değerin gerçek bir Error nesnesi olup olmadığını güvenilir şekilde kontrol etmenizi sağlar. instanceof farklı realm'lar (iframe, worker) arasında çalışmazken, Error.isError() her yerde çalışır.
Sonuç
Etkili hata yönetimi, güvenilir JavaScript uygulamalarının temelini oluşturur. try/catch/finally ile temel hata yakalama, custom error sınıfları ile anlamlı hata hiyerarşisi, asenkron hata yönetimi pattern'leri ve merkezi hata işleme stratejileri ile uygulamalarınızı dayanıklı hale getirebilirsiniz. Hataları anlamlı mesajlarla fırlatmayı, türe göre ayırt etmeyi ve kullanıcıya uygun geri bildirim vermeyi unutmayın.