İçeriğe geç
academia.sh

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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat