Ders 08 / 17
Eşzamansız Hata Yönetimi
Reddetmenin zincirde yayılması, try/catch ile catch yazımının ayrıştığı noktalar, hata bilgisinin kaybı, neden zinciri ve işlenmeyen reddetmelerin izlenmesi.
İçindekiler
Önceki ders hata yolunu bilinçli olarak dışarıda bıraktı. Oysa eşzamansız kodda hata, eşzamanlı koddakinden farklı davranır: yığıt çoktan boşalmış olduğu için hatanın “yukarıya” fırlayacağı bir çağıran yoktur. Reddetme, yığıt yerine sözün kendisi üzerinden taşınır.
Bu ders reddetmenin nasıl yayıldığını, hangi yazımın hangi hatayı yakaladığını ve hiç kimsenin işlemediği bir reddetmenin ne olduğunu kurar. Ölçüm akışı burada arızalı istasyonlara dayanıklı hale gelir.
Reddetme try/catch ile Yakalanır
await ile beklenen bir söz reddedilirse, bekleme noktasında bir hata atılmış gibi
davranılır. Bu, try/catch/finally yapısını eşzamansız kodda yeniden kullanılabilir
kılan tek kuraldır.
Kaynak tablosuna ikinci bir arızalı istasyon eklendi; E5 daha kısa gecikmeyle
reddediyor. Bu, hata sırasının önemli olduğu örneklerde kullanılacak.
const KAYNAK = { A1: { gecikme: 30, deger: 21.4 }, B2: { gecikme: 10, deger: 19.8 }, C3: { gecikme: 20, deger: 23.1 }, D4: { gecikme: 15, hata: "sensör arızası" }, E5: { gecikme: 5, hata: "bağlantı yok" }, }; function olcumGetir(istasyon) { return new Promise((karar, red) => { const kayit = KAYNAK[istasyon]; setTimeout(() => { if (kayit.hata) red(new Error(`${istasyon}: ${kayit.hata}`)); else karar({ istasyon, deger: kayit.deger }); }, kayit.gecikme); }); } try { const olcum = await olcumGetir("D4"); console.log("bu satır çalışmaz:", olcum.deger); } catch (hata) { console.log("yakalandı:", hata.message); } finally { console.log("finally: kaynak serbest bırakıldı"); }
yakalandı: D4: sensör arızası finally: kaynak serbest bırakıldı
finally bloğu eşzamanlı koddaki anlamını korur: sonuç ne olursa olsun çalışır ve
sonucu değiştirmez. Açılmış bir kaynağın kapatılması, kurulmuş bir zamanlayıcının
iptali gibi temizlik işleri buraya yazılır.
thenin İkinci Parametresi ile catch Aynı Şey Değil
Zincir yazımında hata işleyici iki yere konabilir ve ikisi farklı şeyler yakalar.
then(basari, hata) biçiminde verilen ikinci geri çağrı, yalnız kendisinden önceki
sözün reddetmesini işler; aynı then çağrısındaki başarı geri çağrısının attığı hatayı
işlemez.
Promise.resolve("ham değer") .then( () => { throw new Error("işleme adımında hata"); }, (hata) => console.log("bu geri çağrı çalışmaz:", hata.message), ) .catch((hata) => console.log("zincirin sonunda yakalandı:", hata.message));
zincirin sonunda yakalandı: işleme adımında hata
Kural şudur: hata işleyici, hatanın oluştuğu adımdan sonra gelmelidir. Bu nedenle
varsayılan seçim, zincirin sonuna tek bir catch koymaktır; iki parametreli biçim ancak
“bu adımın kendi hatasını başkası işlesin” denmek istendiğinde anlamlıdır.
Bekleyerek Döndürmek
async bir fonksiyonda return olcumGetir(...) ile return await olcumGetir(...)
çoğu bağlamda aynı sonucu verir. try bloğunun içinde vermez.
const KAYNAK = { D4: { gecikme: 15, hata: "sensör arızası" }, }; function olcumGetir(istasyon) { return new Promise((karar, red) => { const kayit = KAYNAK[istasyon]; setTimeout(() => { if (kayit.hata) red(new Error(`${istasyon}: ${kayit.hata}`)); else karar({ istasyon, deger: kayit.deger }); }, kayit.gecikme); }); } async function beklemedenDondur(istasyon) { try { return olcumGetir(istasyon); } catch (hata) { return "catch çalıştı"; } } async function bekleyerekDondur(istasyon) { try { return await olcumGetir(istasyon); } catch (hata) { return "catch çalıştı"; } } console.log("bekleyerek:", await bekleyerekDondur("D4")); try { await beklemedenDondur("D4"); } catch (hata) { console.log("beklemeden: hata dışarı sızdı —", hata.message); }
bekleyerek: catch çalıştı beklemeden: hata dışarı sızdı — D4: sensör arızası
Beklenmeden döndürülen sözde fonksiyon, söz sonuçlanmadan önce geri döner; try bloğu o
anda kapanmıştır ve reddetme çağırana geçer. Kural: bir sözü kendi try bloğunuzda
yakalamak istiyorsanız, döndürmeden önce beklemelisiniz.
Hata Bilgisinin Kaybı
Birleştiriciler hatayı farklı biçimlerde taşır. Promise.all yalnız ilk reddetmeyi
verir; diğerleri sessizce kaybolur.
const KAYNAK = { D4: { gecikme: 15, hata: "sensör arızası" }, E5: { gecikme: 5, hata: "bağlantı yok" }, }; function olcumGetir(istasyon) { return new Promise((karar, red) => { const kayit = KAYNAK[istasyon]; setTimeout(() => { if (kayit.hata) red(new Error(`${istasyon}: ${kayit.hata}`)); else karar({ istasyon, deger: kayit.deger }); }, kayit.gecikme); }); } try { await Promise.all([olcumGetir("D4"), olcumGetir("E5")]); } catch (hata) { console.log("all yalnız ilk hatayı verir:", hata.message); } const sonuclar = await Promise.allSettled([olcumGetir("D4"), olcumGetir("E5")]); const hatalar = sonuclar.filter((s) => s.status === "rejected").map((s) => s.reason.message); console.log("allSettled hepsini verir:", hatalar.join(" | "));
all yalnız ilk hatayı verir: E5: bağlantı yok allSettled hepsini verir: D4: sensör arızası | E5: bağlantı yok
İki istasyon da arızalıydı; all yalnız daha erken reddedeni bildirdi. Tanı için bütün
hataların gerekli olduğu durumlarda — örneğin hangi istasyonların düştüğünü kütüğe yazmak
gerektiğinde — allSettled kullanılmalıdır.
Neden Zinciri
Bir hatayı yakalayıp yerine daha anlamlı bir hata atmak yaygındır; ancak kök neden
kaybolursa tanı zorlaşır. Hata nesnesinin cause seçeneği, alt düzey hatayı üst düzey
hataya iliştirir.
const KAYNAK = { D4: { gecikme: 15, hata: "sensör arızası" }, }; function olcumGetir(istasyon) { return new Promise((karar, red) => { const kayit = KAYNAK[istasyon]; setTimeout(() => { if (kayit.hata) red(new Error(`${istasyon}: ${kayit.hata}`)); else karar({ istasyon, deger: kayit.deger }); }, kayit.gecikme); }); } async function olcumOku(istasyon) { try { return await olcumGetir(istasyon); } catch (hata) { throw new Error(`istasyon okunamadı: ${istasyon}`, { cause: hata }); } } try { await olcumOku("D4"); } catch (hata) { console.log("üst düzey hata:", hata.message); console.log("kök neden:", hata.cause.message); }
üst düzey hata: istasyon okunamadı: D4 kök neden: D4: sensör arızası
Bu kalıp, katmanlar arasında bağlam eklerken bilgi kaybını önler: üst katman “hangi istasyon” der, alt katman “neden” der, ikisi tek nesnede taşınır.
İşlenmeyen Reddetmeler
Bir söz reddedilir ve ona hiçbir hata işleyici bağlanmamışsa, ortaya hiçbir yerde yakalanmayan bir hata çıkar. Çalışma zamanı bunu izler ve bildirir.
const KAYNAK = { D4: { gecikme: 15, hata: "sensör arızası" }, }; function olcumGetir(istasyon) { return new Promise((karar, red) => { const kayit = KAYNAK[istasyon]; setTimeout(() => { if (kayit.hata) red(new Error(`${istasyon}: ${kayit.hata}`)); else karar({ istasyon, deger: kayit.deger }); }, kayit.gecikme); }); } process.on("unhandledRejection", (neden) => { console.log("1 — işlenmeyen reddetme bildirildi:", neden.message); }); process.on("rejectionHandled", () => { console.log("3 — reddetme sonradan işlendi"); }); const soz = olcumGetir("D4"); setTimeout(() => soz.catch((hata) => console.log("2 — geç yakalama:", hata.message)), 50);
1 — işlenmeyen reddetme bildirildi: D4: sensör arızası 2 — geç yakalama: D4: sensör arızası 3 — reddetme sonradan işlendi
İşleyici sonradan bağlandı ve hata gerçekten yakalandı; ama çalışma zamanı bu arada “işlenmeyen reddetme” bildirimini çoktan yapmıştı. Bildirim, mikro görev kuyruğu boşaldığında hâlâ işleyicisiz kalan reddetmeler için üretilir.
Bunun iki pratik sonucu vardır. Birincisi, hata işleyici sözü oluşturduğunuz yerde bağlanmalıdır; sonraya bırakmak yanlış alarm üretir. İkincisi, dinleyici hiç kaydedilmemişse çalışma zamanı varsayılan davranışını uygular: sunucu ortamında tanı iletisi yazılır ve süreç sıfırdan farklı bir çıkış koduyla sonlanır. Bu nedenle işlenmeyen reddetme, kütüğe yazılıp geçilecek bir uyarı değil, düzeltilmesi gereken bir kusurdur.
Dayanıklı Ölçüm Toplama
Bu dersin araçları birleştirildiğinde, ölçüm akışının arızaya dayanıklı biçimi ortaya
çıkar: her istasyon için sonuç ya da nedeni bilinen bir başarısızlık üretilir, hiçbir hata
sessizce kaybolmaz ve toplama tek bir arıza yüzünden durmaz. Kalan tek boşluk, hiç yanıt
vermeyen bir istasyondur; onu ne catch ne allSettled çözer, çünkü ortada reddedilen
bir söz yoktur. Bu boşluk zaman aşımı düzeneğiyle kapatılacak.
Özet
- Reddetme yığıt üzerinden değil söz üzerinden taşınır;
awaitile beklenen bir söz reddedildiğinde bekleme noktasında hata atılmış gibi davranılır. thenin ikinci parametresi yalnız önceki adımın hatasını işler; zincirin sonuna konancatchkendinden önceki tüm adımları kapsar.- Bir sözü kendi
trybloğunda yakalamak için döndürmeden önce beklemek gerekir. allyalnız ilk reddetmeyi taşır; bütün hatalar gerekiyorsaallSettledkullanılır.causeseçeneği kök nedeni koruyarak hata sarmalamayı mümkün kılar.- İşlenmeyen reddetme, mikro görev kuyruğu boşaldığında işleyicisiz kalan reddetmedir ve çalışma zamanı tarafından bildirilir.
Sonraki Adım
Buraya kadar her ölçüm tek seferlik bir sonuçtu: iste, bekle, al. Ölçüm istasyonları ise
sürekli veri üretir; sonucu tek bir söz değil, ucu açık bir dizi temsil eder. Sonraki ders
bu diziyi ele alır: eşzamansız yineleyici protokolü, for await döngüsü, eşzamansız
üreteçler ve tüketicinin üreticiden yavaş olduğu durumda ortaya çıkan geri basınç sorunu.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.