Menu

JavaScript try/catch: Hataları Yakalama Rehberi

JavaScript'te try/catch/finally nasıl çalışır? Hata nesnesi, hatayı tekrar fırlatma (rethrow) ve try/catch'in yanlış tercih olduğu durumlar.

try/catch Güvenlik Ağıdır, Emniyet Kemeri Değil

JavaScript'te bir satır hata fırlattığı anda çalışma orada duruyor ve hata çağrı yığınında (call stack) yukarı doğru yayılıyor. Eğer bu hatayı kimse yakalamazsa program çöküyor (Node tarafında) ya da tarayıcı konsolunda kırmızı bir duvar olarak karşınıza çıkıyor. İşte try/catch tam burada devreye giriyor: "Bu kısım patlayabilir, patlarsa şunu yapmanı istiyorum" demenin yolu bu. JavaScript hata yakalama işini büyük ölçüde bu yapıyla halledeceksiniz.

Temel kalıp şöyle:

index.js
Output
Click Run to see the output here.

JSON.parse bir SyntaxError fırlatır. Akış anında catch bloğuna atlar ve hata err değişkenine bağlanır. Üçüncü console.log yine de çalışır — yani çökme kontrol altına alınmıştır.

try bloğu hata fırlatmadan başarıyla tamamlanırsa, catch bloğu tamamen atlanır. Zaten orada olma sebebi, hata senaryosunu yakalamak.

Error Nesnesi

catch (...) içine yazdığın parametre adına, fırlatılan ne varsa o bağlanır. Çoğu zaman bu, işine yarayacak üç alana sahip bir Error örneği olur:

index.js
Output
Click Run to see the output here.

name sana hatanın alt sınıfını söyler (TypeError, RangeError, SyntaxError gibi — bunlara bir sonraki dokümanda detaylı değineceğiz). message insan tarafından okunabilen açıklamadır. stack ise tüm çağrı izini verir; hata ayıklarken resmen altın değerindedir.

Dikkat edilmesi gereken bir nokta var: JavaScript, sadece Error nesnelerini değil, istediğin her şeyi throw etmene izin verir. Eski kodlarda bazen throw "bir şeyler bozuldu" gibi kullanımlar görürsün. Ama kendi throw ifadeni yazarken her zaman bir Error fırlat ki çağıran taraf stack trace'e ulaşabilsin:

index.js
Output
Click Run to see the output here.

finally Her Durumda Çalışır

finally, isteğe bağlı üçüncü bir bloktur ve hata fırlatılmış olsun olmasın, catch bu hatayı yakalamış olsun olmasın her durumda çalışır. Temizlik işleri için biçilmiş kaftandır — dosyaları kapatmak, kilitleri bırakmak, yükleniyor göstergelerini gizlemek gibi:

index.js
Output
Click Run to see the output here.

Yükleme başarılı da olsa başarısız da olsa spinner gizleniyor. finally olmasaydı bu satırı her iki dala da yazman gerekirdi — ve büyük ihtimalle birinde unuturdun.

finally, try veya catch bloğunda return olsa bile yine de çalışır. Fonksiyon, finally bittikten sonra geri döner. Bu bazen kafa karıştırabilir; ama çoğu zaman tam olarak istediğin davranış budur.

catch Her Zaman Gerekli Değil

catch opsiyoneldir. Sadece try/finally kullanmak hem geçerli hem de oldukça kullanışlıdır; özellikle hatayı yakalamak gibi bir niyetin yoksa ama temizlik işleminin mutlaka yapılmasını istiyorsan — hatanın yukarı fırlamasına izin verirsin:

index.js
Output
Click Run to see the output here.

İçteki try/finally, fn() hata fırlatsa bile kilidi serbest bırakır, ama hatayı yutmaz — çağıran taraf hatayı yine görür. Hataları sessizce yutmak ("patladı ama kimseye söylemedim") hata ayıklamada karşılaşabileceğin en büyük kâbuslardan biridir.

Hatayı yeniden fırlatma: bir kısmını yakala, gerisini üst katmana devret

Bir catch bloğunun her şeyi tek başına halletmesi gerekmez. Hatayı inceleyebilir, anlamlı olanı kendin çözebilir, geri kalanını da yukarıya yeniden fırlatabilirsin:

index.js
Output
Click Run to see the output here.

instanceof kontrolü aslında şu kalıbı temsil ediyor: toparlamayı bildiğin hataları tanı, gerisini çağrı zincirinde yukarı bırak. Her hatayı boş bir catch bloğuyla yutmak ise ciddi bir kod kokusudur — beklenmedik bir şey patladığında elindeki tüm sinyali kaybedersin.

async/await ile try catch kullanımı

Bir async fonksiyon içinde, await ettiğin bir promise reject olduğunda bu fırlatılan bir hataya dönüşür — ve try/catch bunu aynen senkron hatalarda olduğu gibi yakalar:

index.js
Output
Click Run to see the output here.

Dikkat edilmesi gereken bir nokta var: promise'i mutlaka try bloğunun içinde await etmelisiniz. Eğer promise'i await etmeden döndürürseniz, reddedilme olayı fonksiyon çoktan sonlandıktan sonra gerçekleşir ve catch bu hatayı asla yakalayamaz:

async function bad() {
  try {
    return fetch("/broken");  // await yok — çağıran reddi görür
  } catch (err) {
    // asla çalışmaz
  }
}

Pratik bir kural: async fonksiyonlarda try/catch ile yakalamak istediğin işi mutlaka await ile bekle.

İç içe try catch kullanımı

İç ve dış kod farklı sebeplerle hata fırlatıyorsa ve bu hataları ayrı ayrı ele almak istiyorsan, try/catch bloklarını iç içe yazabilirsin:

index.js
Output
Click Run to see the output here.

İçteki catch, "verinin yapısı hatalı" durumunu güvenli bir varsayılan döndürerek ele alıyor. Dıştaki ise "girdi zaten JSON değilmiş" durumunu yakalayıp sarmalayarak yeniden fırlatıyor. Her katmanın farklı bir kurtarma stratejisi olduğunda iç içe try/catch kullanmak gayet mantıklı — ama iki blok da aynı şeyi yapacaksa, tek bir bloğa indirgeyin.

try/catch Ne Zaman Kullanılmamalı?

try/catch, beklenen ve kurtarılabilir hatalar için bir araçtır. Bug'ların üstünü örtmek için değil.

  • Fonksiyonun tüm gövdesini "ne olur ne olmaz" diye sarmalamayın. Hata için somut bir planınız yoksa, yukarı kabarmasına izin verin — stack trace'i olan yakalanmamış bir hata, sessizce yutulmuş bir hatadan çok daha değerlidir.
  • Akış kontrolü için kullanmayın. try bloklarının gerçek bir maliyeti vardır ve if kontrolüne göre kodu bulanıklaştırır. if (user) ifadesi, try { user.name } catch {} yazmaktan her zaman daha iyidir.
  • Yakalayıp "logla ve yut" yapmayın. En azından hatayı yeniden fırlatın ya da çağıranın fark edebileceği bir sentinel değer döndürün.

Akılda tutulacak test şu: "Bu kod çalışmadığında kullanıcısı ne yapacak?" Cevabınız yoksa, hatayı catch etmeye henüz hazır değilsiniz demektir.

Hızlı Başvuru

  • try { ... } catch (err) { ... } — fırlatılan hataları yakalar.
  • finally { ... } — her durumda çalışır; temizlik işleri için kullanın.
  • throw new Error("...") — stack trace'in düzgün çalışması için daima Error alt sınıflarını fırlatın.
  • catch içinde throw err; — ele alamayacağınız durumda hatayı yeniden fırlatın.
  • try içinde awaittry/catch'in async reject'lerini görebilmesi için şart.

Sırada: Hata Türleri

TypeError, RangeError, SyntaxError... JavaScript'in hazır gelen bir hata sınıfı ailesi var ve hangisinin ne anlama geldiğini bilmek, hata yakalama ve raporlamayı çok daha isabetli hale getiriyor. Bir sonraki dokümanın konusu tam olarak bu.

Sıkça Sorulan Sorular

JavaScript'te try/catch nasıl çalışır?

Riskli kodu try { ... } bloğunun içine yazarsın. İçeride herhangi bir yerde hata fırlatılırsa akış doğrudan catch (err) { ... } bloğuna atlar ve fırlatılan değer err parametresine bağlanır. Hata oluşmazsa catch bloğu atlanır. İsteğe bağlı finally { ... } bloğu ise her iki durumda da çalışır; temizlik işleri için birebirdir.

try/catch'i ne zaman kullanmalıyım?

Çalışma anında gerçekten patlayabilecek işlemlerin etrafında kullan: güvenilmez veriyle JSON.parse, fetch yanıtları, dosya ya da ağ I/O gibi. Her satırı sarmalamaya çalışma; hatadan kurtulmak için bir planın yoksa hatanın yukarı kabarmasına izin ver. Sağlam kodun etrafına gelişigüzel konmuş geniş try/catch blokları hataları yönetmez, gizler.

try/catch async hataları yakalar mı?

Yalnızca promise'i try bloğunun içinde await edersen yakalar. Çıplak bir somePromise() çağrısı yakalanmaz; hata unhandled rejection olarak patlar. async/await ile try/catch, eşzamanlı koddaki gibi sorunsuz çalışır. Ham promise zincirlerinde ise .catch() metodunu kullanman gerekir.

JavaScript'te bir hatayı nasıl yeniden fırlatırım?

catch bloğunun içinde basitçe throw err; yazman yeterli (ya da hatayı saran yeni bir hata fırlatabilirsin). Bu yaklaşım bazı hataları kendin ele alıp diğerlerini yukarıya iletmek istediğinde işe yarar: önce hatanın tipine veya mesajına bak, elinden geleni hallet, gerisini yeniden fırlat ki üst katmanlar da haberdar olsun.

Coddy ile kodlamayı öğren

BAŞLA