Ders 15 / 22
Yükleme ve Hata Durumları
Görünümün veri beklerken bulunabileceği durumların sayılı bir kümeye indirgenmesi; mantıksal bayrakların ürettiği çelişkiler, geçiş tablosu, yarış koşullarının damgayla elenmesi ve durumların ekrana karşılığı.
İçindekiler
Önceki üç ders veriyi almanın yollarını kurdu. Hepsinin ortak yanı, verinin bir süre henüz gelmemiş olmasıdır. Ölçüm geçmişi ekranı o sırada boş duramaz: bir şey göstermek zorunda. Gösterilecek şey, ekranın o an hangi durumda olduğuna bağlıdır — ilk kez mi yükleniyor, elde eski veri var mı, istek başarısız mı oldu, sonuç boş mu döndü.
Bu ders o durumları sayılı bir kümeye indirger. İndirgeme bir düzen kaygısı değil: aşağıda görüleceği gibi, durumları ayrı ayrı mantıksal bayraklarla tutmak birbiriyle çelişen bileşimler üretir ve bu bileşimler ekranda gerçek kusur olarak görünür.
Mantıksal Bayrakların Ürettiği Çelişki
En sık başlanan yol üç bayraktır: yukleniyor, hata, veri. Üç mantıksal değerin
sekiz bileşimi vardır. Bunların yalnız dördü anlamlıdır.
- Yükleniyor doğru, veri boş, hata yok — ilk yükleme. Anlamlı.
- Yükleniyor yanlış, veri dolu, hata yok — hazır. Anlamlı.
- Yükleniyor yanlış, veri boş, hata var — başarısız. Anlamlı.
- Yükleniyor doğru, veri dolu, hata yok — tazeleme. Anlamlı.
- Yükleniyor doğru, hata var — istek sürüyor ama hata gösteriliyor. Ekranda dönen bir yükleme göstergesiyle bir hata kutusu yan yana durur.
- Yükleniyor yanlış, veri boş, hata yok — hiçbir şey söylemez. Ekran boştur; kullanıcı isteğin başlamadığını mı, bittiğini ve sonuç bulunmadığını mı bilemez.
- Veri dolu, hata var — hangisi gösterilecek? Bu karar her bileşende yeniden verilir ve farklı verilir.
Bayrakların sorunu, birbirinden bağımsız değişebilmeleridir. Yeni bir istek başlarken eski hatayı temizlemeyi unutan tek bir satır, beşinci bileşimi üretir. Kusur derleme zamanında görünmez, yalnız belirli bir tıklama sırasında ortaya çıkar.
Çözüm, üç bayrağı tek bir alanla değiştirmektir: görünüm aynı anda tek bir durumdadır ve durumlar arasında yalnız tanımlı geçişler vardır. Geçersiz bileşim böylece temsil edilemez hâle gelir.
Yedi Durum ve Geçiş Tablosu
Veri çeken bir ekranın ihtiyaç duyduğu durumlar şunlardır: boş (henüz istenmedi), yükleniyor (elde veri yokken bekleniyor), hazır (veri var), boş sonuç (istek başarılı, sonuç kümesi boş), yenileniyor (elde veri varken tazeleniyor), hata (elde veri yok, istek başarısız) ve bayat (elde eski veri var, tazeleme başarısız).
Son iki durumun ayrılması bu tasarımın özüdür. Elde gösterilecek veri varken tazeleme başarısız olduğunda ekranı hata kutusuna çevirmek, kullanıcının elindeki bilgiyi almak demektir. Bayat durumu eski veriyi ekranda tutar ve tazelemenin başarısız olduğunu ayrı bir bildirimle söyler.
// gorunum-durumu.mjs — gorunum durum makinesi ve gecis tablosu const DURUMLAR = ["bos", "yukleniyor", "hazir", "bosSonuc", "yenileniyor", "hata", "bayat"]; const OLAYLAR = ["iste", "doluGeldi", "bosGeldi", "basarisiz", "iptal"]; // Veriye bagli hedefler islev olarak yazilir. const doluysaHazir = (b) => (b.veri.length > 0 ? "hazir" : "bosSonuc"); const TABLO = { bos: { iste: "yukleniyor" }, yukleniyor: { doluGeldi: "hazir", bosGeldi: "bosSonuc", basarisiz: "hata", iptal: "bos" }, hazir: { iste: "yenileniyor" }, bosSonuc: { iste: "yenileniyor" }, yenileniyor: { doluGeldi: "hazir", bosGeldi: "bosSonuc", basarisiz: "bayat", iptal: doluysaHazir }, hata: { iste: "yukleniyor" }, bayat: { iste: "yenileniyor" }, }; function gecis(birim, olay, yuk = null) { const hedef = TABLO[birim.durum]?.[olay]; if (hedef === undefined) return { ...birim, yoksayilan: birim.yoksayilan + 1 }; const yeni = { ...birim }; if (olay === "doluGeldi") yeni.veri = yuk; if (olay === "bosGeldi") yeni.veri = []; if (olay === "basarisiz") yeni.hata = yuk; if (olay === "doluGeldi" || olay === "bosGeldi") yeni.hata = null; yeni.durum = typeof hedef === "function" ? hedef(yeni) : hedef; return yeni; } // --- Gecis tablosunun yazdirilmasi ----------------------------------------- const hucre = (d, o) => { const h = TABLO[d]?.[o]; return h === undefined ? "·" : typeof h === "function" ? "veriye bağlı" : h; }; console.log("durum".padEnd(12) + OLAYLAR.map((o) => o.padEnd(13)).join("")); for (const d of DURUMLAR) { console.log(d.padEnd(12) + OLAYLAR.map((o) => hucre(d, o).padEnd(13)).join("")); } // --- Dizilerin sinanmasi ---------------------------------------------------- const OLCUM = [{ kimlik: "o-114", deger: -4.2 }]; const DIZILER = [ ["ilk yükleme başarılı", [["iste"], ["doluGeldi", OLCUM]]], ["ilk yükleme boş", [["iste"], ["bosGeldi"]]], ["ilk yükleme hatalı", [["iste"], ["basarisiz", "ag"]]], ["hatadan sonra tekrar", [["iste"], ["basarisiz", "ag"], ["iste"], ["doluGeldi", OLCUM]]], ["tazeleme hatası", [["iste"], ["doluGeldi", OLCUM], ["iste"], ["basarisiz", "sunucu"]]], ["tazeleme iptali", [["iste"], ["doluGeldi", OLCUM], ["iste"], ["iptal"]]], ["geçersiz sıra", [["doluGeldi", OLCUM], ["iptal"], ["iste"]]], ]; console.log(""); for (const [ad, olaylar] of DIZILER) { let birim = { durum: "bos", veri: [], hata: null, yoksayilan: 0 }; const iz = ["bos"]; for (const [olay, yuk] of olaylar) { birim = gecis(birim, olay, yuk); iz.push(birim.durum); } console.log( ad.padEnd(22), iz.join(" → ").padEnd(46), `veri=${birim.veri.length} hata=${birim.hata} yoksayılan=${birim.yoksayilan}`); } // --- Yaris korumasi: gec gelen yanit yok sayilir --------------------------- console.log(""); let birim = { durum: "bos", veri: [], hata: null, yoksayilan: 0 }; let damga = 0; const istek = () => { damga += 1; birim = gecis(birim, "iste"); return damga; }; const yanit = (kendiDamgasi, olay, yuk) => { if (kendiDamgasi !== damga) { console.log(` damga ${kendiDamgasi}: eskimiş, yok sayıldı`); return; } birim = gecis(birim, olay, yuk); console.log(` damga ${kendiDamgasi}: uygulandı → ${birim.durum}`); }; const d1 = istek(); // "kuz" icin arama const d2 = istek(); // "kuzey" icin arama yanit(d2, "doluGeldi", [{ kimlik: "o-114" }, { kimlik: "o-113" }]); yanit(d1, "doluGeldi", [{ kimlik: "o-001" }]); // gec gelen eski yanit console.log("son durum:", birim.durum, "| kayıt:", birim.veri.length);
durum iste doluGeldi bosGeldi basarisiz iptal bos yukleniyor · · · · yukleniyor · hazir bosSonuc hata bos hazir yenileniyor · · · · bosSonuc yenileniyor · · · · yenileniyor · hazir bosSonuc bayat veriye bağlı hata yukleniyor · · · · bayat yenileniyor · · · · ilk yükleme başarılı bos → yukleniyor → hazir veri=1 hata=null yoksayılan=0 ilk yükleme boş bos → yukleniyor → bosSonuc veri=0 hata=null yoksayılan=0 ilk yükleme hatalı bos → yukleniyor → hata veri=0 hata=ag yoksayılan=0 hatadan sonra tekrar bos → yukleniyor → hata → yukleniyor → hazir veri=1 hata=null yoksayılan=0 tazeleme hatası bos → yukleniyor → hazir → yenileniyor → bayat veri=1 hata=sunucu yoksayılan=0 tazeleme iptali bos → yukleniyor → hazir → yenileniyor → hazir veri=1 hata=null yoksayılan=0 geçersiz sıra bos → bos → bos → yukleniyor veri=0 hata=null yoksayılan=2 damga 2: uygulandı → hazir damga 1: eskimiş, yok sayıldı son durum: hazir | kayıt: 2
Tablodan Okunanlar
Tablodaki nokta işaretleri tanımsız geçişleri gösterir ve asıl bilgi oradadır.
hazir durumundayken basarisiz olayı gelemez, çünkü hazır durumdayken uçuşta bir istek
yoktur. Bu, kodun bir yerinde açıkça yasaklanmış bir kural değil; tablonun boş bıraktığı
bir hücre. Yedinci dizi bunu sınıyor: istek atılmadan gelen yanıt ve iptal olayları yok
sayıldı, sayaç ikiyi gösterdi. Bayrak tabanlı bir gerçekleştirimde aynı iki olay durumu
sessizce bozardı.
Beşinci ve altıncı diziler yenileniyor durumunun iki farklı çıkışını gösteriyor.
Başarısızlıkta bayata geçildi ve veri=1 korundu — eski ölçüm listesi ekranda kaldı.
İptalde hazira dönüldü ve hata alanı boş kaldı; önceki dersin kuralı burada işliyor,
iptal bir hata değildir.
Dördüncü dizi hata sonrası kurtarmayı gösteriyor. hata durumundan yalnız iste çıkar;
“tekrar dene” düğmesinin karşılığı budur. Yeni istek başlarken hata alanının temizlenmesi
geçiş işlevinin içinde tek bir yerde yapılır, her çağrı noktasında değil.
Durum makinesinin yan faydası sınanabilirliktir. Yukarıdaki yedi dizi bir birim testi kümesidir; ekran hiç çizilmeden görünümün davranışı doğrulanabilir. Bileşenin içine serpiştirilmiş bayraklarda bu ayrıştırma mümkün değildir.
Yarış Koşulu ve Damga
Son bölüm, REST İstemcisi dersinde bırakılan sorunun görünüm tarafındaki yarısını kapatıyor. Kullanıcı istasyon süzgecine “kuz” yazdı, sonra “kuzey” yazdı; iki istek yola çıktı. İkinci istek daha önce yanıtlandı, birincisi geç geldi. Geç gelen yanıt uygulanırsa ekranda “kuz” sonuçları görünür ama süzgeç kutusunda “kuzey” yazar.
Çözüm bir istek damgasıdır: her yeni istek bir sayaç artırır ve kendi damgasını taşır. Yanıt geldiğinde damga, o an geçerli olan damgayla karşılaştırılır; eşleşmiyorsa yanıt uygulanmaz. Çıktının son üç satırı bunu gösteriyor: ikinci istek uygulandı, birincisi eskimiş sayıldı, ekranda iki kayıt kaldı.
Damga, isteğin iptal edilmesinin yerine geçmez; ikisi birlikte kullanılır. İptal ağ kaynağını serbest bırakır ama iptal edilemeyen bir isteğin — örneğin yanıtı zaten yola çıkmış bir isteğin — geç gelmesini engellemez. Damga, o son savunmadır.
Aynı düzenek anahtar bazında da kurulabilir: sayaç yerine “hangi süzgeç için” bilgisi saklanır ve gelen yanıt kendi anahtarıyla eşleşmiyorsa atılır. Sunucu durumu yönetiminde önbellek anahtarı zaten bu işi görür; damga, önbellek kullanmayan ekranlar için aynı korumayı sağlar.
Durumların Ekrana Karşılığı
Durum kümesi belirlendikten sonra her durumun görsel karşılığı ayrı bir karardır. Arayüz Tasarımının Temelleri kursundaki Yükleme ve Boş Durumlar ile Hata ve Uyarı Durumları dersleri bu kararların ölçütlerini kurar; burada yalnız durum makinesine bağlanan yanları anılır.
Yükleniyor durumunda gösterilecek şey, beklenen içeriğin biçimine bağlıdır. Yapısı bilinen bir liste için yer tutucu bir iskelet, gelen içeriğin kaplayacağı alanı önceden ayırır ve içerik geldiğinde kayma oluşmaz. Yapısı bilinmeyen bir sonuç için nötr bir gösterge yeterlidir.
Yenileniyor durumu, yukleniyor ile aynı görünmemelidir. Elde veri varken ekranı
iskelete çevirmek, kullanıcının okuduğu içeriği ondan almaktır. Doğru sunum, mevcut
içeriği yerinde bırakıp tazelemeyi küçük ve kenarda bir göstergeyle bildirmektir.
Boş sonuç ile hata ayrı iletiler ister. Boş sonuç sistemin doğru çalıştığını ve ölçütü karşılayan kayıt bulunmadığını söyler; kurtarma yolu ölçütü değiştirmektir. Hata, sistemin yanıt veremediğini söyler; kurtarma yolu tekrar denemektir. İkisini aynı kutuda sunmak, kullanıcıyı yanlış eyleme yönlendirir.
Bayat durumu iki bilgiyi aynı anda taşır: ekrandaki veri geçerlidir ama güncel olmayabilir. Sunumu, veriyi yerinde bırakıp üstünde tazelemenin başarısız olduğunu ve son başarılı güncellemenin ne zaman yapıldığını bildiren bir şeritle yapılır. “Tekrar dene” eylemi bu şeritte durur.
Bir de sürenin kendisi bir karardır. Yanıt çok hızlı geldiğinde yükleme göstergesini
göstermek, ekranda anlık bir titreme üretir; gösterge küçük bir gecikmeden sonra
görünecek biçimde ertelenir. Gösterge bir kez göründüyse de en az kısa bir süre ekranda
kalır, yoksa aynı titreme çıkışta yaşanır. Bu iki eşik durum makinesinin dışındadır:
makine yukleniyor der, göstergeyi ne zaman çizeceğine sunum katmanı karar verir.
Özet
- Yükleme, hata ve veri bilgisini ayrı mantıksal bayraklarla tutmak sekiz bileşim üretir; yarısı çelişkilidir ve bu çelişkiler ekranda gerçek kusur olarak görünür.
- Tek alanlı bir durum makinesi geçersiz bileşimleri temsil edilemez kılar; tablodaki boş hücreler tanımsız geçişleri gösterir ve beklenmeyen olay sessizce yok sayılır.
- Elde veri varken tazelemenin başarısız olması ayrı bir durumdur; ekranı hata kutusuna çevirmek yerine eski veri korunur ve tazeleme başarısızlığı ayrıca bildirilir.
- Boş sonuç ile hata farklı kurtarma yolları önerir: biri ölçütü değiştirmeyi, diğeri tekrar denemeyi.
- İstek damgası geç gelen yanıtı eler; iptalin yerine geçmez, onunla birlikte çalışır.
- Yükleme göstergesinin görünme ve en az kalma eşikleri sunum katmanının kararıdır; durum makinesi yalnız hangi durumda olunduğunu söyler.
Sonraki Adım
Durum makinesi basarisiz olayını tek bir kapı olarak ele aldı: hata geldi, duruma
geçildi, kullanıcıya “tekrar dene” düğmesi sunuldu. Oysa hataların bir kısmı kullanıcıya
hiç gösterilmemeli. Zayıf bir bağlantıda kesilen bir istek, iki yüz milisaniye sonra
yeniden denendiğinde başarılı olabilir; kullanıcı arada hiçbir şey görmez. Hangi hatanın
kendiliğinden yeniden deneneceği, denemeler arasında ne kadar bekleneceği ve bu beklemenin
neden rastgeleleştirilmesi gerektiği sonraki dersin konusudur.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.