Ders 16 / 21
Hata Yönetimi
`try`/`catch`/`finally` akışı, `throw` deyimi, yerleşik hata tipleri, hata nesnesinin alanları, neden zinciri ve hata ile dönüş değeri arasındaki seçim.
İçindekiler
Önceki derslerde try/catch yapısı hata iletilerini göstermek için kullanıldı ama
tanımlanmadı. Bu ders yapıyı tamamlar: blokların akışı, hata fırlatma, yerleşik hata
tipleri ve — en önemlisi — bir sorunun hata mı yoksa dönüş değeri mi olarak
bildirileceği kararı.
Kod Çalıştırma Yolları dersinde hata nesnesinin ad, ileti ve yığıt izi taşıdığı gösterilmişti. Burada bu nesneleri kendimiz üreteceğiz.
Blokların Akışı
try bloğu izlenen kodu içerir. Hata oluşursa akış catch bloğuna atlar. finally
bloğu, hata olsun olmasın çalışır.
function dene(deger) { try { console.log("1 try basi"); if (deger < 0) throw new Error("negatif deger"); console.log("2 try sonu"); return "try donusu"; } catch (hata) { console.log("3 catch:", hata.message); return "catch donusu"; } finally { console.log("4 finally"); } } console.log(dene(1)); console.log("---"); console.log(dene(-1));
1 try basi 2 try sonu 4 finally try donusu --- 1 try basi 3 catch: negatif deger 4 finally catch donusu
İki gözlem önemlidir. Birincisi, hata oluştuğunda try bloğunun kalan satırları
çalışmaz; akış doğrudan catch’e geçer. İkincisi, finally bloğu return deyiminden
sonra ama fonksiyon gerçekten dönmeden önce çalışır. Bu yüzden temizlik işleri
oraya yazılır: açılan bir kaynağın kapatılması, ayrılan bir kilidin bırakılması.
finally içinde return yazmak, dönüş değerini ezer:
function ezer() { try { return "try"; } finally { return "finally"; } } console.log(ezer());
finally
Aynı davranış fırlatılan hatayı da yutar. Bu yüzden finally bloğu yalnızca temizlik
içerir; akışı yönlendiren bir deyim taşımaz.
Hata nesnesine ihtiyaç yoksa yakalama bağı yazılmayabilir:
function gecerliJson(metin) { try { JSON.parse(metin); return true; } catch { return false; } } console.log(gecerliJson('{"a":1}'), gecerliJson("{bozuk}"));
true false
Yerleşik Hata Tipleri
Standart birkaç hata tipi tanımlar ve dilin kendi ürettiği hatalar bunlardan biridir:
const denemeler = [ () => null.x, () => bilinmeyenAd, () => ([].length = -1), () => (1234).toFixed(200), ]; for (const d of denemeler) { try { d(); } catch (hata) { console.log(hata.name + ": " + hata.message); } }
TypeError: Cannot read properties of null (reading 'x') ReferenceError: bilinmeyenAd is not defined RangeError: Invalid array length RangeError: toFixed() digits argument must be between 0 and 100
TypeError bir değerin beklenen türde olmadığını, ReferenceError bir adın
bulunamadığını, RangeError bir değerin izin verilen aralığın dışında olduğunu bildirir.
SyntaxError ise ayrıştırma sırasında üretilir; ilk derste görüldüğü gibi programı hiç
başlatmaz — tek istisnası, metin ayrıştıran yerleşik yöntemlerin çalışma zamanında
ürettiği durumdur.
Hata adları standarttır; iletiler değildir. İleti metinleri çalışma zamanları arasında ve sürümler arasında değişir. Bu yüzden kodda ileti metnine göre dallanılmaz; ada, tipe veya kendi eklediğimiz bir alana bakılır.
Hata Fırlatmak
throw deyimi herhangi bir değeri fırlatabilir. Fırlatılan şeyin hata nesnesi olması
zorunlu değildir, ama olmalıdır:
try { throw "duz metin"; } catch (hata) { console.log(typeof hata, hata); console.log("name:", hata.name, "| stack:", typeof hata.stack); } try { throw new Error("hata nesnesi"); } catch (hata) { console.log(typeof hata, hata instanceof Error); console.log("name:", hata.name, "| stack:", typeof hata.stack); }
string duz metin name: undefined | stack: undefined object true name: Error | stack: string
Metin fırlatıldığında ad ve yığıt izi yoktur; hatanın nereden geldiği kaybolur. Kursun kuralı: fırlatılan her değer bir hata nesnesidir.
Alt katmandaki bir hatayı sararken özgün hata korunmalıdır. Hata nesnesi bunun için bir neden (cause) alanı taşıyabilir:
function sicaklikCevir(metin) { const deger = Number(metin); if (Number.isNaN(deger)) { throw new Error(`sayiya cevrilemedi: ${JSON.stringify(metin)}`); } return deger; } function kaydiOku(satir) { const [istasyon, sicaklikMetni] = satir.split(";"); try { return { istasyon, sicaklik: sicaklikCevir(sicaklikMetni) }; } catch (hata) { throw new Error(`kayit okunamadi: ${istasyon}`, { cause: hata }); } } console.log(kaydiOku("A1;21.5")); try { kaydiOku("B2;abc"); } catch (hata) { console.log(hata.message); console.log("neden:", hata.cause.message); }
{ istasyon: 'A1', sicaklik: 21.5 }
kayit okunamadi: B2
neden: sayiya cevrilemedi: "abc"
Üst katman kendi bağlamını ekledi (hangi kayıt), alt katmanın bilgisi (neden) kayboldu sayılmadı. Bu, hata iletilerini bağlamla zenginleştirmenin standart yoludur.
Kendi Hata Tipini İşaretlemek
Bir hatayı kendi ürettiğimizi ayırt edebilmek gerekir; aksi halde catch bloğu, kodun
kendi hatalarıyla birlikte beklenmedik programlama hatalarını da yutar.
En yalın yol, hata nesnesine ad ve bir kod alanı eklemektir:
function olcumHatasi(kod, ileti) { const hata = new Error(ileti); hata.name = "OlcumHatasi"; hata.kod = kod; return hata; } const h = olcumHatasi("BOS_ALAN", "sicaklik alani bos"); console.log(h.name, "|", h.kod, "|", h.message, "|", h instanceof Error);
OlcumHatasi | BOS_ALAN | sicaklik alani bos | true
Kalıtımla adlandırılmış hata sınıfları tanımlamak — sınıf sözdizimi kullanarak — bir sonraki kursun konusudur. Bu kursta ad ve kod alanı yeterlidir.
Ölçüm Betiğine Uygulama
Bozuk veriye verilen yanıt artık bir sözleşmeye bağlanabilir: her satır ya kabul edilir ya da gerekçesiyle reddedilir; hiçbir satır sessizce yanlış değere dönüşmez.
const KOD_BOS = "BOS_ALAN"; const KOD_SAYI = "SAYI_DEGIL"; function olcumHatasi(kod, ileti) { const hata = new Error(ileti); hata.name = "OlcumHatasi"; hata.kod = kod; return hata; } function sicaklikOku(metin) { if (metin.trim() === "") { throw olcumHatasi(KOD_BOS, "sicaklik alani bos"); } const deger = Number(metin); if (Number.isNaN(deger)) { throw olcumHatasi(KOD_SAYI, `sicaklik sayiya cevrilemedi: ${JSON.stringify(metin)}`); } return deger; } const satirlar = ["A1;21.5", "A2;", "B1;abc", "B2;0"]; const kabul = []; const red = []; for (const satir of satirlar) { const [istasyon, alan] = satir.split(";"); try { kabul.push({ istasyon, sicaklik: sicaklikOku(alan) }); } catch (hata) { if (hata.name !== "OlcumHatasi") throw hata; red.push({ istasyon, kod: hata.kod, ileti: hata.message }); } } console.log("kabul:", kabul); console.log("red:", red);
kabul: [ { istasyon: 'A1', sicaklik: 21.5 }, { istasyon: 'B2', sicaklik: 0 } ]
red: [
{ istasyon: 'A2', kod: 'BOS_ALAN', ileti: 'sicaklik alani bos' },
{
istasyon: 'B1',
kod: 'SAYI_DEGIL',
ileti: 'sicaklik sayiya cevrilemedi: "abc"'
}
]
Üç karar bu betiği önceki sürümlerinden ayırıyor.
Birincisi, B2 kaydındaki 0 değeri kabul edildi. Doğruluk Değerleri dersinde kurulan
ayrım korunuyor: sıfır geçerli bir ölçümdür.
İkincisi, yakalama bloğu kendi hatasını tanıyor. hata.name !== "OlcumHatasi"
denetimi, beklenmedik bir hatayı yeniden fırlatıyor. Bu satır olmasaydı, örneğin
satir.split çağrısında oluşacak bir TypeError de “reddedilen kayıt” sayılır ve
programlama hatası veri hatası gibi görünürdü.
Üçüncüsü, red listesi gerekçe taşıyor. Kaç kaydın neden reddedildiği çıktıdan okunabiliyor.
Hata mı, Dönüş Değeri mi
Her olağandışı durum hata değildir. Ölçüt şudur: çağıran taraf durumu öngörebiliyorsa dönüş değeri, öngöremiyorsa hata uygundur.
Boş bir listenin ortalamasını istemek öngörülebilir bir durumdur; Fonksiyon Bildirimi ve
İfadesi dersindeki ortalama fonksiyonu bu yüzden null döndürdü. Buna karşılık,
sayısal olması gereken bir alanda harf bulunması verinin sözleşmesinin ihlalidir; hata
uygundur.
İki uygulama da yukarıdaki betikte bir arada bulunuyor: sicaklikOku hata fırlatıyor,
çağıran döngü hatayı yakalayıp bir veri yapısına — red listesine — çeviriyor. Sınır,
sorunun nerede ele alınabileceğine göre çizilir.
Hatalar akış denetimi için kullanılmaz. Bir döngüden çıkmak, bir değeri atlamak veya bir
koşulu bildirmek için hata fırlatmak, hem maliyetli hem de okunması güç bir kalıptır;
break, continue ve dönüş değerleri bu iş içindir.
Özet
trybloğunda hata oluşursa kalan satırlar çalışmaz;finallyher durumda vereturndeğerlendirildikten sonra çalışır.finallyiçindereturnveyathrowyazmak, dönüş değerini ve hatayı ezer; blok yalnızca temizlik içerir.- Hata adları standarttır, iletiler değildir; dallanma ileti metnine göre yapılmaz.
- Fırlatılan her değer hata nesnesi olmalıdır; aksi halde ad ve yığıt izi bulunmaz.
- Sarmalanan hatalar
causealanıyla korunur; kendi hataları ad ve kod alanıyla işaretlenir ve tanınmayan hatalar yeniden fırlatılır. - Öngörülebilir durum dönüş değeriyle, sözleşme ihlali hatayla bildirilir.
Sonraki Adım
Denetim akışı ve fonksiyonlar tamamlandı. Bu konudaki örneklerde filter, map, split
ve nesne yayma gibi yapılar açıklanmadan kullanıldı. Sonraki konu bunları tanımlayacak:
dizilerin oluşturma ve dönüştürme yöntemleri, nesne erişim kısayolları, şablon dizgileri,
JSON dönüşümleri ve tarih–zaman gösterimi. Ölçüm listesi orada metinden okunup metne geri
yazılan tam bir veri kümesine dönüşecek.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.