JavaScript'te hata yönetimi, throw ile bir istisna fırlatmak ve onu try...catch ile yakalamak üzerine kuruludur. try bloğunda bir hata oluştuğunda kontrol hemen catch bloğuna geçer; finally bloğu ise hata olsa da olmasa da en sonda çalışır. Fırlatılan değer olarak her zaman bir Error nesnesi (veya TypeError, RangeError gibi bir alt sınıfı) kullanmanız önerilir.
Bu yazıda try/catch/finally kurallarını, yerleşik hata türlerini, hatayı sarmalarken kökeni korumayı sağlayan error.cause özelliğini ve kendi hata sınıflarınızı yazmayı anlatıyoruz. Asenkron koddaki hatalar için async/await yazımızı, konsolda gördüğünüz mesajların anlamı için sık hata mesajları rehberini tamamlayıcı olarak okuyabilirsiniz.
try, catch ve finally

try ifadesi her zaman bir try bloğuyla başlar ve ardından en az bir catch veya finally bloğu gelmelidir. Böylece üç biçim ortaya çıkar: try...catch, try...finally ve try...catch...finally. if'ten farklı olarak bu bloklar süslü parantezsiz tek bir ifade olamaz.
function bol(a, b) {
if (b === 0) throw new RangeError('Bölen sıfır olamaz');
return a / b;
}
try {
console.log(bol(10, 2)); // 5
console.log(bol(1, 0));
console.log('buraya gelinmez');
} catch (e) {
console.log(e.name, '-', e.message); // RangeError - Bölen sıfır olamaz
} finally {
console.log('finally her durumda çalışır');
}Hata try bloğunun doğrudan içinde değil, oradan çağrılan bir fonksiyonun içinde fırlatılsa bile aynı catch tarafından yakalanır. İç içe try ifadelerinde içteki bloğun catch'i yoksa dıştakinin catch'i devreye girer.
catch bağlamı: değişken, destructuring ve parametresiz catch
catch (e) içindeki e fırlatılan değeri tutar ve yalnızca catch bloğu içinde geçerlidir. Tek bir isim yerine destructuring kalıbı da kullanabilirsiniz. Hata değerine ihtiyacınız yoksa parantezleri tamamen atlayabilirsiniz:
try {
throw new TypeError('oops');
} catch ({ name, message }) {
console.log(name, message); // TypeError oops
}
function gecerliJSON(metin) {
try {
JSON.parse(metin);
return true;
} catch {
return false;
}
}
console.log(gecerliJSON('[1,2]'), gecerliJSON('[1,')); // true falseDestructuring sözdizimi için destructuring ve spread yazımıza, JSON ayrıştırma hataları için JSON.parse ve stringify rehberine bakabilirsiniz.
finally bloğu nasıl çalışır?
finally bloğu, kontrol try...catch...finally yapısından çıkmadan önce mutlaka çalışır. try veya catch içinde return, throw, break ya da continue varsa bunların etkisi finally bitene kadar ertelenir. Dönüş değeri finally'den önce hesaplanır ama fonksiyon ancak finally bittikten sonra döner.
function f() {
try {
return 'try';
} finally {
console.log('finally önce çalışır');
}
}
console.log(f());
// finally önce çalışır
// tryfunction g() {
try {
throw new Error('kaybolacak');
} finally {
return 'finally kazandı';
}
}
console.log(g()); // finally kazandıthrow: neyi fırlatmalısınız?
throw herhangi bir ifadeyi fırlatabilir: metin, sayı, nesne. Ancak MDN, pratikte her zaman bir Error nesnesi ya da alt sınıfı fırlatmanızı önerir, çünkü hatayı yakalayan kod message, name ve yığın izi gibi özellikleri bekler. Düz metin fırlatırsanız bu özellikler olmaz:
try {
throw 'sadece metin';
} catch (e) {
console.log(typeof e, e.message); // string undefined
}Bir sözdizimi ayrıntısı: throw anahtar sözcüğü ile fırlatılan ifade arasında satır sonu olamaz. throw yazıp ifadeyi alt satıra geçirirseniz otomatik noktalı virgül ekleme (ASI) devreye girer ve kod geçersiz hale gelir.
Yerleşik hata türleri

Genel Error yapıcısının yanında JavaScript'in çeşitli yerleşik hata türleri vardır. Hepsi name ve message özelliklerine sahiptir. Aşağıdaki kod her birinin gerçek bir örneğini tetikler:
const denemeler = [
() => null.ad, // null üzerinde özellik okuma
() => tanimsizDegisken, // tanımlanmamış değişken
() => new Array(-1), // geçersiz dizi uzunluğu
() => JSON.parse('{bozuk'), // geçersiz JSON
() => decodeURIComponent('%'), // bozuk URI
];
for (const f of denemeler) {
try { f(); } catch (e) { console.log(e.constructor.name); }
}
// TypeError
// ReferenceError
// RangeError
// SyntaxError
// URIError| Tür | Ne zaman oluşur? (MDN) |
|---|---|
| TypeError | Değer veya parametre beklenen türde değil |
| ReferenceError | Geçersiz bir referansa erişim |
| RangeError | Sayısal değer geçerli aralığın dışında |
| SyntaxError | Sözdizimi hatası (JSON.parse dahil) |
| URIError | encodeURI veya decodeURI geçersiz parametre aldı |
| EvalError | eval() ile ilgili hata |
| AggregateError | Birden fazla hata tek nesnede, örneğin Promise.any |
Bir Error nesnesinde neler var?
Her hata nesnesinin message özelliği yapıcıya verdiğiniz metni, name özelliği ise türün adını tutar. toString() bu ikisini "ad: mesaj" biçiminde birleştirir. stack özelliği MDN'de standart dışı olarak işaretlidir ama Node.js dahil yaygın motorlarda bir metin olarak bulunur. Alt sınıflar hem kendi türlerinin hem de Error'ın örneğidir:
const e = new RangeError('Değer çok büyük');
console.log(e.name); // RangeError
console.log(e.message); // Değer çok büyük
console.log(e.toString()); // RangeError: Değer çok büyük
console.log(e instanceof RangeError, e instanceof Error); // true true
console.log(typeof e.stack); // stringMDN ayrıca Firefox'a özgü, standart dışı bir InternalError türünden söz eder. Belirli bir türü yakalamak için instanceof kullanın; tanımadığınız hataları yeniden fırlatın (throw e), böylece üst katman onları görebilir.
error.cause: hatayı sarmalarken kökeni korumak
Düşük seviyeli bir hatayı yakalayıp daha anlamlı bir mesajla yeniden fırlatmak yaygın bir kalıptır. Sorun, orijinal hatanın ve yığın izinin kaybolmasıdır. Error yapıcısının ikinci parametresindeki cause seçeneği bunu çözer: orijinal hata yeni hatanın cause özelliğinde saklanır.
function ayarOku(metin) {
try {
return JSON.parse(metin);
} catch (err) {
throw new Error('Ayar dosyası okunamadı', { cause: err });
}
}
try {
ayarOku('{port: 80}');
} catch (e) {
console.log(e.message); // Ayar dosyası okunamadı
console.log(e.cause instanceof SyntaxError); // true
}cause herhangi bir türde olabilir. MDN, kütüphane yazarlarının hata mesajını ayrıştırmak yerine makinenin okuyabileceği yapılandırılmış veriyi cause olarak vermesini önerir. cause seçeneği verilmezse özellik hiç oluşturulmaz:
const e = new Error('Zaman aşımı', { cause: { kod: 'E_ZAMAN', sure: 5000 } });
console.log(e.cause.kod); // E_ZAMAN
console.log('cause' in new Error('yok')); // falseÖzel hata sınıfları yazmak
Error sınıfını genişleterek kendi hata türlerinizi tanımlayabilir, onları instanceof ile ayırt edebilirsiniz. cause desteğini korumak için options parametresini super()'e iletmeyi unutmayın. name özelliğini ayarlamak, hatanın metne çevrildiğinde doğru adla görünmesini sağlar.
class DogrulamaHatasi extends Error {
constructor(alan, message, options) {
super(message, options);
this.name = 'DogrulamaHatasi';
this.alan = alan;
}
}
function kaydet(kullanici) {
if (!kullanici.eposta) throw new DogrulamaHatasi('eposta', 'E-posta zorunlu');
return 'kaydedildi';
}
try {
kaydet({ ad: 'Can' });
} catch (e) {
if (e instanceof DogrulamaHatasi) {
console.log(`${e.name}: ${e.alan} alanı - ${e.message}`);
} else {
throw e;
}
}
// DogrulamaHatasi: eposta alanı - E-posta zorunlu
console.log(String(new DogrulamaHatasi('x', 'deneme'))); // DogrulamaHatasi: deneme
console.log(Error.isError(new DogrulamaHatasi('x', 'y'))); // true
console.log(Error.isError({ name: 'Error', message: 'sahte' })); // falseError.isError(), bir değerin gerçekten Error olup olmadığını denetler. MDN'e göre instanceof Error'dan daha sağlamdır: başka bir realm'de (örneğin bir iframe) oluşturulan hataları da tanır, prototipi taklit edilmiş sahte nesneleri ise reddeder. Görece yeni bir yöntem olduğu için MDN uyumluluk tablosuna bakın. Sınıf sözdizimi için class yazımıza göz atabilirsiniz.
Asenkron kodda hata yakalamak
try...catch yalnızca kendi bloğu çalışırken oluşan hataları yakalar. Bir callback daha sonra (örneğin setTimeout ile) çalışıp hata fırlatırsa o anda try bloğu çoktan bitmiştir:
try {
setTimeout(() => { throw new Error('geç'); }, 0);
} catch (e) {
console.log('buraya hiç gelinmez');
}
process.on('uncaughtException', (e) => console.log('global yakalandı:', e.message));
// global yakalandı: geçPromise tabanlı kodda hatayı catch() ile, async fonksiyonlarda ise await ile birlikte try...catch kullanarak yakalarsınız. Bu konuyu Promise ve async/await yazılarında anlattık. Birden fazla Promise'in hatasını toplayan AggregateError için Promise.any karşılaştırmasına bakın.
Sık yapılan hatalar

- Hatayı yutmak: boş bir
catch {}sorunu gizler. En azından kaydedin veya tanımadığınız hatayı yeniden fırlatın. - Metin fırlatmak:
throw 'hata'yerinethrow new Error('hata')kullanın. - Yeniden fırlatırken kökeni kaybetmek:
{ cause: err }seçeneğini ekleyin. - finally içinde return: önceki dönüşü ve hatayı ezer.
- Mesaj metnine göre dallanmak: mesajlar değişebilir; özel sınıf veya yapılandırılmış
causekullanın. - Asenkron callback'i dıştaki try ile korumaya çalışmak: hata o blok bittikten sonra oluşur.
Sık Sorulan Sorular
catch bloğu hangi hataları yakalar?
try bloğu çalışırken, doğrudan veya çağrılan fonksiyonlar içinde fırlatılan her değeri yakalar. Sözdizimi hataları (kod hiç ayrıştırılamıyorsa) ve sonradan çalışan asenkron callback'lerdeki hatalar bu kapsama girmez.
Belirli bir hata türünü nasıl yakalarım?
JavaScript'te koşullu catch sözdizimi yoktur. Tek bir catch içinde instanceof ile türü kontrol edin ve eşleşmeyen hataları throw e ile yeniden fırlatın.
error.stack standart mı?
MDN, stack özelliğini standart dışı olarak işaretler; yine de yaygın motorlarda bulunur. Biçimi motora göre değişebileceği için ayrıştırmaya dayanan kod yazmayın.
cause ile AggregateError arasındaki fark nedir?
cause tek bir hata noktasına bağlam ekler. AggregateError aynı işlemde oluşan birbirinden bağımsız birden fazla hatayı bir arada tutar. MDN ayrıca bir hatayı ele alırken ikinci bir hata oluştuğunda kullanılan SuppressedError türünden de söz eder.
Parametresiz catch ne zaman uygun?
Hatanın kendisiyle hiçbir şey yapmayacağınız, yalnızca "başarılı mı, değil mi" sorusunun önemli olduğu durumlarda uygundur. MDN'deki JSON.parse ile geçerlilik denetimi buna iyi bir örnektir. Hatanın ayrıntısını kaydetmeniz gerekiyorsa değişkeni yazın; parametresiz catch hatayı tamamen görmezden gelir.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: JavaScript öğrenme rehberi · Sık hata mesajları · async/await · Promise nedir · Class sınıflar · Strict mode