Ders 19 / 22
Yükleme ve Boş Durumlar
Bekleme süresinin geri bildirim sınıflarına ayrılması, gösterge gecikmesinin titremeye etkisi, iskelet gösteriminin yerleşim kayması puanıyla gerekçelendirilmesi ve boş durumun üç türü.
İçindekiler
Önceki ders bir bileşenin beş etkileşim durumunu tanımladı. Beşi de tek bir varsayıma dayanıyordu: bileşen ekranda vardır ve etkileşime hazırdır. Katalog arayüzünde bu varsayım sık sık geçersizdir. Kullanıcı arama düğmesine bastı ve sonuçlar henüz gelmedi; arama hiçbir kayıt döndürmedi; ya da kullanıcı sayfayı yeni açtı ve hiç arama yapmadı.
Bu üç durumda ekranda gösterilecek bir sonuç listesi yoktur. Tasarlanacak şey listenin kendisi değil, listenin yokluğudur. Bu ders yokluğun iki biçimini ayırır: veri yolda olduğu için oluşan yokluk ve veri olmadığı için oluşan yokluk. İkisi farklı bilgi verir ve farklı çözüm ister.
Bekleme Bir Süre Aralığıdır
Yükleme durumu tek bir tasarım değildir; ne kadar sürdüğüne bağlı olarak farklı bir şey gerektirir. Sürenin kendisi ölçülür ve karar ölçüme dayandırılır.
// bekleme.mjs — olculen yanit surelerinin geri bildirim siniflarina dagitilmasi // Yinelenebilir ornek uretimi: dogrusal esleyici uretec + log-normal donusum function uretec(tohum) { let s = tohum >>> 0; return () => ((s = (1664525 * s + 1013904223) >>> 0) / 4294967296); } const rastgele = uretec(20260727); function normal() { const u1 = Math.max(rastgele(), 1e-12), u2 = rastgele(); return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2); } // Katalog aramasinin yanit suresi (ms): medyani 220 ms, sag kuyruklu const ornekler = Array.from({ length: 200 }, () => Math.round(220 * Math.exp(0.9 * normal())) ).sort((a, b) => a - b); const yuzdelik = (p) => ornekler[Math.min(ornekler.length - 1, Math.floor((p / 100) * ornekler.length))]; console.log(`ornek sayisi: ${ornekler.length}`); console.log(`en kucuk ${ornekler[0]} ms medyan ${yuzdelik(50)} ms y90 ${yuzdelik(90)} ms y99 ${yuzdelik(99)} ms en buyuk ${ornekler[ornekler.length - 1]} ms`); // Geri bildirim siniflari: her sinifin ust siniri ve gerektirdigi gosterim const SINIFLAR = [ { ad: "anlik", ust: 100, gosterim: "geri bildirim gerekmez" }, { ad: "kesintisiz", ust: 1000, gosterim: "imlec/dugme durumu yeter" }, { ad: "fark edilir", ust: 10000, gosterim: "yukleme gostergesi" }, { ad: "uzun", ust: Infinity, gosterim: "ilerleme + iptal" }, ]; console.log("\nsinif ust sinir(ms) ornek sayisi oran gerekli gosterim"); for (const s of SINIFLAR) { const alt = SINIFLAR[SINIFLAR.indexOf(s) - 1]?.ust ?? 0; const n = ornekler.filter((v) => v > alt && v <= s.ust).length; console.log( `${s.ad.padEnd(12)} ${(s.ust === Infinity ? "-" : String(s.ust)).padStart(13)} ${String(n).padStart(13)} ${(n / ornekler.length * 100).toFixed(1).padStart(5)}% ${s.gosterim}` ); } // Gosterge gecikmesi: gosterge kac istekte acilir, kacinda titreme uretir? // Titreme olcutu: gosterge 200 ms'den kisa sure gorunur. console.log("\ngecikme(ms) gosterge acilan titreyen titreme orani (tum istekler)"); for (const gecikme of [0, 150, 300, 500]) { const acilan = ornekler.filter((v) => v > gecikme); const titreyen = acilan.filter((v) => v - gecikme < 200).length; console.log( `${String(gecikme).padStart(11)} ${String(acilan.length).padStart(16)} ${String(titreyen).padStart(9)} ${(titreyen / ornekler.length * 100).toFixed(1).padStart(28)}%` ); } // En az gorunme suresi kurali: gosterge acildiysa en az 300 ms kalir. // Bu, bazi isteklere yapay gecikme ekler; toplam maliyeti hesaplanir. const GECIKME = 300, EN_AZ = 300; let yapay = 0, etkilenen = 0; for (const v of ornekler) { if (v <= GECIKME) continue; const gorunme = v - GECIKME; if (gorunme < EN_AZ) { yapay += EN_AZ - gorunme; etkilenen++; } } console.log(`\n300 ms gecikme + 300 ms en az gorunme:`); console.log(` yapay gecikme eklenen istek sayisi: ${etkilenen} / ${ornekler.length}`); console.log(` toplam eklenen sure: ${yapay} ms, istek basina ortalama: ${(yapay / ornekler.length).toFixed(1)} ms`);
ornek sayisi: 200
en kucuk 23 ms medyan 228 ms y90 722 ms y99 2100 ms en buyuk 2362 ms
sinif ust sinir(ms) ornek sayisi oran gerekli gosterim
anlik 100 45 22.5% geri bildirim gerekmez
kesintisiz 1000 147 73.5% imlec/dugme durumu yeter
fark edilir 10000 8 4.0% yukleme gostergesi
uzun - 0 0.0% ilerleme + iptal
gecikme(ms) gosterge acilan titreyen titreme orani (tum istekler)
0 200 89 44.5%
150 126 59 29.5%
300 75 34 17.0%
500 41 20 10.0%
300 ms gecikme + 300 ms en az gorunme:
yapay gecikme eklenen istek sayisi: 47 / 200
toplam eklenen sure: 7573 ms, istek basina ortalama: 37.9 ms
Sınıf sınırları insan algısıyla ilgili yerleşik eşiklerdir. Yüz milisaniyenin altındaki bir yanıt anlık algılanır; bir saniyeye kadar olan yanıt akışı kesmez ama fark edilir; on saniyeye kadar olan bekleme dikkatin dağılmasına yol açar; onun ötesi ise kullanıcının başka bir işe geçtiği aralıktır.
Ölçüm bir tasarım kararını doğrudan veriyor: isteklerin yüzde 96’sı bir saniyenin altında tamamlanıyor. Yani katalog aramasının çoğunlukla bir yükleme göstergesine ihtiyacı yok. Gösterge her istekte açılırsa, gösterge kendisi bir gürültü kaynağı hâline gelir.
İkinci tablo bunu sayıya çeviriyor. Gösterge gecikmesiz açıldığında isteklerin yüzde 44.5’i göstergenin 200 milisaniyeden kısa süre görünüp kaybolmasına yol açıyor; ekranda beliren ve hemen kaybolan bir öge, bilgi vermek yerine dikkat çeker. Gecikme 300 milisaniyeye çekildiğinde bu oran yüzde 17’ye, 500 milisaniyede yüzde 10’a iniyor.
Gecikme tek başına yetmez, çünkü gösterge açıldıktan hemen sonra da kapanabilir. İkinci kural, gösterge bir kez açıldıysa en az bir süre görünür kalmasıdır. Üçüncü blok bunun maliyetini veriyor: 300 milisaniyelik en az görünme kuralı 200 isteğin 47’sine yapay gecikme ekliyor ve istek başına ortalama 37.9 milisaniye maliyet üretiyor. Bu maliyet bilinçli ödenir: titremeyi ortadan kaldırmanın bedeli, bazı isteklerin biraz daha uzun görünmesidir.
Üçüncü satırdaki “ilerleme + iptal” sınıfında hiç örnek yok. Bu, katalog aramasının o sınıfa girmediği anlamına gelir; ancak arayüzün başka bir işlemi — toplu dışa aktarma gibi — o sınıfa girerse ilerleme göstergesi ve iptal düğmesi zorunlu olur. Belirsiz bir gösterge, on saniyeyi aşan işlemlerde kullanıcıya işlemin ilerleyip ilerlemediğini söylemez.
İskelet Gösterimi Yer Ayırır
Yükleme göstergesinin ikinci işi, gelecek içeriğin yerini tutmaktır. Küçük bir dönen gösterge bu işi yapmaz: içerik geldiğinde altındaki her şey aşağı itilir ve kullanıcının baktığı nokta kayar.
Bu kayma ölçülebilir. Yerleşim kayması puanı (layout shift score), kayan ögelerin etkilediği alanın görüntü alanına oranı ile ögelerin kat ettiği uzaklığın görüntü alanının en büyük kenarına oranının çarpımıdır.
// iskelet.mjs — iskelet gosteriminin satir sayisi ve yerlesim kaymasi puani const GORUNTU = { genislik: 1280, yukseklik: 800 }; const BASLIK_Y = 96; // ust baslik seridi const SATIR_Y = 72; // sonuc satiri yuksekligi const ALTLIK_Y = 80; // altlik seridi // 1) Gorunur alana kac iskelet satiri sigar? const alan = GORUNTU.yukseklik - BASLIK_Y; console.log(`gorunur icerik alani: ${alan} px`); console.log(`satir yuksekligi: ${SATIR_Y} px`); console.log(`tam sigan satir sayisi: ${Math.floor(alan / SATIR_Y)}`); console.log(`kismen gorunen satir: ${(alan % SATIR_Y) > 0 ? "var (" + (alan % SATIR_Y) + " px)" : "yok"}`); // 2) Yerlesim kaymasi puani: etki kesri x uzaklik kesri // Kararsiz oge: altlik seridi. Yukleme gorunumunden yuklenmis gorunume gecerken kayar. function kaymaPuani(oncekiUst, sonrakiUst) { const g = GORUNTU.yukseklik; const gorunurAralik = (ust) => { const alt = Math.min(g, ust + ALTLIK_Y); return ust >= g ? [0, 0] : [Math.max(0, ust), alt]; }; const [o1, o2] = gorunurAralik(oncekiUst); const [s1, s2] = gorunurAralik(sonrakiUst); const birlesimUst = Math.min(o1 || Infinity, s1 || Infinity); const birlesimAlt = Math.max(o2, s2); const etkiKesri = (birlesimAlt - birlesimUst) / g; const uzaklik = Math.abs(sonrakiUst - oncekiUst); const uzaklikKesri = uzaklik / Math.max(GORUNTU.genislik, GORUNTU.yukseklik); return { etkiKesri, uzaklikKesri, puan: etkiKesri * uzaklikKesri, uzaklik }; } const SATIR_SAYISI = 9; // yuklenmis listede gorunur satir sayisi const yuklenmisAltlik = BASLIK_Y + SATIR_SAYISI * SATIR_Y; const SENARYOLAR = [ { ad: "donen gosterge (120 px)", yukseklik: 120 }, { ad: "iskelet 5 satir", yukseklik: 5 * SATIR_Y }, { ad: "iskelet 8 satir", yukseklik: 8 * SATIR_Y }, { ad: "iskelet 9 satir", yukseklik: SATIR_SAYISI * SATIR_Y }, ]; console.log("\nyukleme gorunumu altlik once altlik sonra uzaklik etki kesri uzaklik kesri kayma puani"); for (const s of SENARYOLAR) { const once = BASLIK_Y + s.yukseklik; const k = kaymaPuani(once, yuklenmisAltlik); console.log( `${s.ad.padEnd(25)} ${String(once).padStart(11)} ${String(yuklenmisAltlik).padStart(13)} ${String(k.uzaklik).padStart(8)} ` + `${k.etkiKesri.toFixed(4).padStart(11)} ${k.uzaklikKesri.toFixed(4).padStart(14)} ${k.puan.toFixed(4).padStart(12)}` ); } console.log("\niyi kabul edilen ust sinir: 0.100");
gorunur icerik alani: 704 px satir yuksekligi: 72 px tam sigan satir sayisi: 9 kismen gorunen satir: var (56 px) yukleme gorunumu altlik once altlik sonra uzaklik etki kesri uzaklik kesri kayma puani donen gosterge (120 px) 216 744 528 0.7300 0.4125 0.3011 iskelet 5 satir 456 744 288 0.4300 0.2250 0.0968 iskelet 8 satir 672 744 72 0.1600 0.0563 0.0090 iskelet 9 satir 744 744 0 0.0700 0.0000 0.0000 iyi kabul edilen ust sinir: 0.100
Dönen gösterge 0.3011 puan üretiyor, iyi kabul edilen üst sınırın üç katı. Nedeni, göstergenin gerçek içeriğe göre çok kısa olması ve içerik geldiğinde altlığın 528 piksel aşağı itilmesidir.
İskelet gösteriminin satır sayısı bu puanı doğrudan belirliyor. Beş satır 0.0968 ile sınırın hemen altında kalıyor, sekiz satır 0.0090’a iniyor, dokuz satırda kayma tümüyle ortadan kalkıyor. Görünür alana tam olarak dokuz satır sığdığı için doğru sayı budur.
Buradan çıkan kural şudur: iskelet, gelecek içeriğin sayısını değil, görünür alanın alacağı satır sayısını taklit eder. Arama otuz sonuç döndürecekse bile iskeletin otuz satır çizmesi gereksizdir; görünür alandaki dokuz satır kaymayı sıfırlamaya yeter.
İskelet gösteriminin bir sınırı vardır ve gözden kaçırılır: iskelet, gelecek içeriğin yapısını taahhüt eder. Dokuz satırlık bir iskelet çizilip ardından üç sonuç geldiğinde, kullanıcı bir an için dokuz sonuç olduğunu sanmış olur. Bu yüzden iskelet yalnızca sonucun geleceği kesin olduğunda kullanılır; sonuç sayısının sıfır olma olasılığı yüksek bir aramada iskelet yerine yer tutan boş bir alan uygundur.
Boş Durum Bir Hata Değildir
Yüklemenin bittiği ama içeriğin olmadığı durum, üç ayrı nedenden doğar ve üçü farklı şey söyler.
İlk kullanım boşluğu. Kullanıcı katalog sayfasını yeni açtı ve henüz arama yapmadı. Burada eksik olan veri değil, kullanıcının eylemi. Doğru içerik bir açıklama ve bir başlangıç yoludur: aramanın neyi kapsadığı ve hangi alanlarda arama yapılabildiği.
Sonuçsuz arama boşluğu. Kullanıcı arama yaptı, sistem çalıştı, eşleşen kayıt yok. Burada eksik olan veri. Doğru içerik, aramanın ne olduğunu tekrarlamak ve daraltmayı gevşetecek bir yol sunmaktır: uygulanan filtrelerin listesi ve tek tek kaldırma seçeneği. Arama metnini tekrarlamak bir ayrıntı değil, gerekliliktir; kullanıcının yazdığını yanlış yazdığını görmesinin tek yolu odur.
Temizlenmiş liste boşluğu. Kullanıcı ödünç aldığı bütün kitapları iade etti ve “ödünç aldıklarım” listesi boşaldı. Burada boşluk bir başarıdır. Doğru içerik ne açıklama ne öneridir; durumu bildiren tek bir satır yeterlidir.
Üç durumun ortak yanlışı, boşluğu bir hata gibi sunmaktır. Hata rengi, uyarı simgesi ve “bulunamadı” ifadesi kullanıcıya bir şeyin bozulduğunu söyler; oysa üç durumda da sistem doğru çalışmıştır. İşlevsel Renkler dersindeki ayrım burada uygulanır: boş durum bilgi sınıfına girer, hata sınıfına değil.
Boş durumun ikinci yaygın yanlışı, ekranı tümüyle boş bırakmaktır. Kullanıcı bu durumda yüklemenin sürüp sürmediğini bilemez. Boş durum da bir tasarımdır ve bir metin taşımak zorundadır; hangi metnin nasıl yazılacağı bu konunun son dersinin sorusudur.
Özet
- Bekleme tek bir durum değil, süreye göre farklı gösterim isteyen bir aralıktır; sınıf sınırları ölçülen sürelerle karşılaştırılır.
- Ölçüm çoğu isteğin gösterge eşiğinin altında tamamlandığını gösterirse, gösterge gecikmeli açılır; gecikme titreme oranını doğrudan düşürür.
- Gösterge açıldıktan sonra en az bir süre görünür kalır; bunun bedeli bazı isteklere eklenen yapay gecikmedir ve bu bedel hesaplanabilir.
- İskelet gösterimi yer ayırarak yerleşim kaymasını sıfırlar; doğru satır sayısı gelecek sonuç sayısı değil, görünür alana sığan satır sayısıdır.
- İskelet gelecek içeriğin yapısını taahhüt eder; sonuç gelmeme olasılığı yüksekse iskelet yanıltıcıdır.
- Boş durum üç ayrı nedenden doğar ve hiçbiri hata değildir; boşluk bilgi sınıfında sunulur ve her zaman bir metin taşır.
Sonraki Adım
Boş durum sistemin doğru çalıştığı bir sonuçtu. Sistemin doğru çalışmadığı durumlar ise ayrı bir tasarım gerektirir: ISBN alanına geçersiz bir değer girildi, ödünç alma isteği sunucuya ulaşamadı, kullanıcının ödünç alma hakkı doldu. Bu üçü aynı kutuda sunulamaz, çünkü üçünün de kurtarma yolu farklıdır. Sonraki ders hatayı kaynağına ve kurtarılabilirliğine göre sınıflandırır, iletinin nereye konacağını hata alanına olan uzaklıkla belirler ve hangi hataların hiç gösterilmemesi gerektiğini tartışır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.