Ders 13 / 25
Askıya Alma ve Yükleniyor Durumları
Verisi hazır olmayan bileşenin oluşturmayı kesip en yakın bekleme sınırına sinyal vermesi; elle tutulan bayrakların ürettiği geçersiz bileşimler, sınır yerleşiminin açığa çıkardığı istek şelalesi, gösterge titremesinin ölçüsü ve geçişlerde eski içeriğin korunması.
İçindekiler
Önceki ders bir alt ağacın başarısız olduğu durumu ağacın geri kalanından ayırdı. Ayrılması gereken ikinci bir durum daha vardır: alt ağacın henüz hazır olmadığı durum. Ölçüm tablosu, veriler sunucudan gelene kadar çizilemez; karşılaştırma panosu ikinci istasyonun kayıtlarını bekler.
İki durum arasındaki benzerlik yapısaldır. Her ikisinde de bir bileşen kendi başına çözemediği bir sorunla karşılaşır ve bunu yukarıya bildirir; yukarıdaki bir sınır bildirimi yakalar ve alt ağacın yerine başka bir görünüm koyar. Bu derste ikinci mekanizmaya askıya alma (suspension), onu yakalayan bileşene bekleme sınırı denecektir.
Elle Tutulan Bayrakların Sorunu
Yaygın çözüm her bileşende üç ayrı durum tutmaktır: veri, yükleniyor bayrağı ve hata. Üç ayrı yuva sekiz bileşim üretir ve bunların yalnız üçü anlamlıdır — yükleniyor, hata, veri. Kalan beş bileşim geçersizdir: hem yükleniyor hem hatalı, hem veri var hem yükleniyor, üçü birden boş.
Geçersiz bileşimler kod içinde ortaya çıkmaz, kenar durumlarda çıkar: ikinci istek başlatılırken hata bayrağı sıfırlanmazsa ekranda hem gösterge hem hata iletisi görünür. Bunu önlemenin yolu üç bayrağı tek bir ayrık durumla değiştirmektir; ama o zaman da her bileşen kendi ayrık durumunu, kendi göstergesini ve kendi hata görünümünü taşır. Sayfada altı veri okuyan bileşen varsa altı ayrı gösterge döner.
Askıya alma bu iki sorunu birden çözer: bileşen “hazır değilim” demenin dışında hiçbir durum tutmaz, gösterge kararı yukarıya toplanır.
Askıya Alma
Bileşen, verisini okumaya çalışır. Veri hazırsa değeri alır ve oluşturmayı sürdürür. Hazır değilse isteği başlatır ve oluşturmayı keser. Kesme yukarı çıkar, en yakın bekleme sınırında durur, o sınır alt ağacın yerine bekleme görünümünü koyar. Veri geldiğinde alt ağaç yeniden oluşturulur ve bu kez okuma başarılı olur.
// askiya-alma.mjs — hazir olmayan bilesenin en yakin bekleme sinirina sinyal vermesi const BEKLEME = Symbol("bekleme"); function depo() { const kayit = new Map(); const baslangic = new Map(); // istegin hangi turda baslatildigi let tur = 0; return { turAc: () => { tur++; }, oku(anahtar) { const k = kayit.get(anahtar); if (!k) { kayit.set(anahtar, { hal: "bekliyor" }); baslangic.set(anahtar, tur); // istek ilk okuma denemesinde baslar throw BEKLEME; } if (k.hal === "bekliyor") throw BEKLEME; return k.veri; }, yanitla(anahtar, veri) { kayit.set(anahtar, { hal: "hazir", veri }); }, ozet: () => [...baslangic].map(([a, t]) => `${a}: ${t}. turda`).join(", "), }; } const dugum = (ad, s = {}) => ({ ad, sinir: !!s.sinir, veri: s.veri || null, cocuklar: s.cocuklar || [] }); function olustur(d, kaynak) { if (d.sinir) { try { const p = d.cocuklar.map((c) => olustur(c, kaynak)); return { metin: `${d.ad}[${p.map((x) => x.metin).join(" ")}]`, sayi: 1 + p.reduce((t, x) => t + x.sayi, 0) }; } catch (h) { if (h !== BEKLEME) throw h; return { metin: `${d.ad}[YUKLENIYOR]`, sayi: 1 }; } } const etiket = d.veri ? `${d.ad}<${kaynak.oku(d.veri)}>` : d.ad; if (d.cocuklar.length === 0) return { metin: etiket, sayi: 1 }; const p = d.cocuklar.map((c) => olustur(c, kaynak)); return { metin: `${etiket}(${p.map((x) => x.metin).join(" ")})`, sayi: 1 + p.reduce((t, x) => t + x.sayi, 0) }; } const ayriSinirlar = () => dugum("sayfa", { cocuklar: [ dugum("baslik"), dugum("tablo-siniri", { sinir: true, cocuklar: [dugum("olcum-tablosu", { veri: "olcumler" })] }), dugum("pano-siniri", { sinir: true, cocuklar: [dugum("karsilastirma-panosu", { veri: "karsilastirma" })] }), ] }); const tekSinir = () => dugum("sayfa", { cocuklar: [ dugum("baslik"), dugum("ortak-sinir", { sinir: true, cocuklar: [ dugum("olcum-tablosu", { veri: "olcumler" }), dugum("karsilastirma-panosu", { veri: "karsilastirma" }), ] }), ] }); for (const [ad, kur] of [["iki ayri bekleme siniri", ayriSinirlar], ["tek ortak bekleme siniri", tekSinir]]) { console.log(ad + ":"); const kaynak = depo(); const turlar = [ ["ilk olusturma", null], ["olcumler geldi", "olcumler"], ["karsilastirma geldi", "karsilastirma"], ]; for (const [baslik, gelen] of turlar) { kaynak.turAc(); if (gelen) kaynak.yanitla(gelen, `${gelen} verisi`); const s = olustur(kur(), kaynak); console.log(` ${baslik.padEnd(20)} icerik gosteren bilesen: ${s.sayi}`); console.log(` ${s.metin}`); } console.log(` istek baslangici -> ${kaynak.ozet()}`); }
iki ayri bekleme siniri:
ilk olusturma icerik gosteren bilesen: 4
sayfa(baslik tablo-siniri[YUKLENIYOR] pano-siniri[YUKLENIYOR])
olcumler geldi icerik gosteren bilesen: 5
sayfa(baslik tablo-siniri[olcum-tablosu<olcumler verisi>] pano-siniri[YUKLENIYOR])
karsilastirma geldi icerik gosteren bilesen: 6
sayfa(baslik tablo-siniri[olcum-tablosu<olcumler verisi>] pano-siniri[karsilastirma-panosu<karsilastirma verisi>])
istek baslangici -> olcumler: 1. turda, karsilastirma: 1. turda
tek ortak bekleme siniri:
ilk olusturma icerik gosteren bilesen: 3
sayfa(baslik ortak-sinir[YUKLENIYOR])
olcumler geldi icerik gosteren bilesen: 3
sayfa(baslik ortak-sinir[YUKLENIYOR])
karsilastirma geldi icerik gosteren bilesen: 5
sayfa(baslik ortak-sinir[olcum-tablosu<olcumler verisi> karsilastirma-panosu<karsilastirma verisi>])
istek baslangici -> olcumler: 1. turda, karsilastirma: 2. turda
Sayfa başlığı her iki yerleşimde de ilk turda görünür: beklemek yalnız veri okuyan alt ağacı durdurur, kardeşlerini değil. Bu, hata sınırındaki yalıtımın aynısıdır.
Sınır Yerleşimi ve İstek Şelalesi
İki yerleşim arasındaki fark ikinci turda ortaya çıkar. Ayrı sınırlarda ölçüm tablosu verisi gelir gelmez görünür; ortak sınırda ekran hiç değişmez, çünkü sınırın altındaki bütün ağaç hazır olmadıkça yedek görünüm kalkmaz. Bekleme sınırı, “birlikte görünmesi gereken” parçaları tanımlar.
Çıktının son satırı daha sinsi bir farkı gösterir. Ayrı sınırlarda iki istek de birinci turda başlamıştır. Ortak sınırda ölçüm tablosunun kesmesi kardeşinin oluşturulmasını engellediği için karşılaştırma isteği ancak ikinci turda başlar. İkinci istek, birincinin yanıtını beklemiş olur; iki bağımsız istek ardışıklaşır. Buna istek şelalesi (request waterfall) denir — Tarayıcı ve Web Platformu kursundaki ağ panelinde bu, basamaklı bir şelale görünümü olarak okunur.
Şelalenin ikinci ve daha yaygın biçimi iç içe bileşenlerde ortaya çıkar: üst bileşen istasyon kaydını okur, alt bileşen o kayıttaki kimlikle ölçümleri okur. İkinci istek gerçekten birinciye bağlıdır ve yerleşimle çözülmez. Çözüm, isteği oluşturmanın başlatmasını beklememektir: veri isteği yönlendirme çözümlendiği anda başlatılır, bileşen oluşturulduğunda istek çoktan yoldadır. Kural şudur: veri gereksinimi, oluşturmadan önce bilinebiliyorsa istek oluşturmadan önce başlatılır.
Gösterge Titremesi
Bekleme görünümünün ne zaman gösterileceği ayrı bir karardır ve ölçülebilir.
// gosterge.mjs — yukleniyor gostergesinin ne zaman cikacagi ve ne kadar kalacagi const YANITLAR = [80, 150, 220, 900]; // ms cinsinden yanit sureleri const ESIK = 200; // bu sureden once gelen yanitta gosterge hic cikmaz const EN_AZ = 400; // gosterge ciktiysa en az bu kadar ekranda kalir function hesapla(politika, yanit) { if (politika === "hemen") return { baslar: 0, biter: yanit, icerik: yanit }; if (yanit <= ESIK) return { baslar: null, biter: null, icerik: yanit }; if (politika === "gecikmeli") return { baslar: ESIK, biter: yanit, icerik: yanit }; const biter = Math.max(yanit, ESIK + EN_AZ); // gecikmeli + en az sure return { baslar: ESIK, biter, icerik: biter }; } for (const politika of ["hemen", "gecikmeli", "gecikmeli+en az"]) { console.log(`${politika}:`); for (const yanit of YANITLAR) { const s = hesapla(politika, yanit); const sure = s.baslar === null ? 0 : s.biter - s.baslar; const satir = [ ` yanit ${String(yanit).padStart(3)} ms ->`, `gosterge ${s.baslar === null ? "cikmaz" : `${s.baslar}-${s.biter} ms`}`.padEnd(20), `gorunme suresi ${String(sure).padStart(3)} ms`, `| icerik ${String(s.icerik).padStart(3)} ms'de`, ].join(" "); console.log(sure > 0 && sure < 300 ? `${satir} <- titreme` : satir); } }
hemen: yanit 80 ms -> gosterge 0-80 ms gorunme suresi 80 ms | icerik 80 ms'de <- titreme yanit 150 ms -> gosterge 0-150 ms gorunme suresi 150 ms | icerik 150 ms'de <- titreme yanit 220 ms -> gosterge 0-220 ms gorunme suresi 220 ms | icerik 220 ms'de <- titreme yanit 900 ms -> gosterge 0-900 ms gorunme suresi 900 ms | icerik 900 ms'de gecikmeli: yanit 80 ms -> gosterge cikmaz gorunme suresi 0 ms | icerik 80 ms'de yanit 150 ms -> gosterge cikmaz gorunme suresi 0 ms | icerik 150 ms'de yanit 220 ms -> gosterge 200-220 ms gorunme suresi 20 ms | icerik 220 ms'de <- titreme yanit 900 ms -> gosterge 200-900 ms gorunme suresi 700 ms | icerik 900 ms'de gecikmeli+en az: yanit 80 ms -> gosterge cikmaz gorunme suresi 0 ms | icerik 80 ms'de yanit 150 ms -> gosterge cikmaz gorunme suresi 0 ms | icerik 150 ms'de yanit 220 ms -> gosterge 200-600 ms gorunme suresi 400 ms | icerik 600 ms'de yanit 900 ms -> gosterge 200-900 ms gorunme suresi 700 ms | icerik 900 ms'de
Göstergeyi hemen göstermek, hızlı yanıtlarda ekranda beliren ve hemen kaybolan bir kare üretir. Kullanıcı bunu bir yükleme olarak değil bir sıçrama olarak algılar; iki durum değişimi, tek bir bekleme görünmesinden daha rahatsız edicidir.
Gecikmeli gösterim eşiğin altındaki yanıtlarda göstergeyi hiç çıkarmaz. Ne var ki eşiğin hemen üstündeki yanıtta yirmi milisaniyelik bir titreme bırakır — sorunun en kötü biçimi.
Üçüncü politika bunu en az gösterim süresiyle kapatır ve bedelini görünür kılar: iki yüz yirmi milisaniyede hazır olan içerik altı yüz milisaniyeye kadar bekletilir. Bu, bilinçli olarak ödenen bir gecikmedir; hızlı yanıtın ödülünü kullanıcıya vermez, karşılığında sarsıntısız bir ekran verir. Üç politika arasındaki seçim ölçülen yanıt dağılımına bağlıdır: yanıtların çoğu eşiğin altındaysa ikinci, eşiğin çevresinde toplanıyorsa üçüncü politika seçilir.
Bekleme Görünümünün Tasarımı ve Geçişler
Bekleme görünümünün geleceği içeriğin yerini tutması gerekir. Dönen bir gösterge, yerini alacağı tablodan küçükse veri geldiğinde sayfa aşağı kayar; Web’in Temelleri ve HTML kursunda tanımlanan içerik kaymasıdır bu. Bunun yerine gelecek düzenin ölçülerini taşıyan bir iskelet görünüm konur: aynı satır sayısı, aynı sütun genişlikleri, aynı yükseklik.
İkinci karar geçişlerdedir. İlk yükleme ile sonraki yükleme farklı sorunlardır. Kullanıcı süzgeci değiştirdiğinde ekranda çalışan bir tablo vardır; yedek görünüme düşmek bu tabloyu silip iskelete dönmek olur. Çalışan bir ekranı boşaltmak, kullanıcının bağlamını da siler.
Daha iyi davranış, eski içeriği yerinde tutup bir bekleme göstergesi eklemektir — solmuş bir tablo, başlıkta küçük bir gösterge, devre dışı bırakılmış bir süzgeç. Kural şöyle yazılabilir: bekleme sınırı boş bir alanı doldurmak içindir; dolu bir alanı boşaltmak için değil. Eski içerik gösterilirken onun eski olduğunun belirtilmesi gerekir, yoksa kullanıcı güncellenmiş veri gördüğünü sanır.
Erişilebilirlik tarafı da atlanmaz. Bekleme ve tamamlanma, ekran okuyucuya duyurulmalıdır; görsel bir göstergenin tek başına anlamı yoktur. Devre dışı bırakılan denetimlerin nedeni de metinle bildirilir.
Özet
- Her bileşende ayrı tutulan veri, yükleniyor ve hata bayrakları sekiz bileşim üretir ve bunların beşi geçersizdir.
- Askıya alma, verisi hazır olmayan bileşenin oluşturmayı kesip yukarıya sinyal vermesidir; en yakın bekleme sınırı sinyali yakalar, alt ağacın yerine bekleme görünümü koyar ve kardeş alt ağaçlar oluşturulmayı sürdürür.
- Bekleme sınırı, birlikte görünmesi gereken parçaları tanımlar; tek bir sınırın altında toplanan bağımsız istekler ardışıklaşarak istek şelalesi üretir.
- Veri gereksinimi oluşturmadan önce bilinebiliyorsa istek oluşturmadan önce başlatılır.
- Göstergeyi hemen göstermek titreme üretir; eşikli gösterim kısa yanıtlarda göstergeyi eler, en az gösterim süresi titremeyi kapatır ama içeriği geciktirir.
- Bekleme görünümü gelecek içeriğin ölçülerini taşımalıdır; süzgeç değişimi gibi geçişlerde çalışan içerik silinmez, eski olduğu belirtilerek korunur.
Sonraki Adım
Buraya kadarki yedi ders bir alt ağacın ne zaman oluşturulacağını belirledi: durum değiştiğinde, bağımlılık değiştiğinde, hata yakalandığında, veri geldiğinde. Bir soru daha kaldı ve o zamanla değil yerle ilgili: alt ağaç nereye çizilir?
Varsayılan yanıt açıktır — bileşen ağacındaki yeri, belge ağacındaki yerini belirler. Ölçüm istasyonu sayfasında bu varsayılanın kırıldığı bir nokta vardır. Süzgeç panelindeki açılır liste, panelin kendi kutusundan taşar; kabın kırpması listeyi keser, kabın yığılma bağlamı listeyi tablonun altında bırakır. Bileşen mantıksal olarak panelin içindedir ama görsel olarak sayfanın en üstünde çizilmelidir. Sonraki ders bu ayrılığı, iki ağacın birbirinden bağımsız hale gelmesini ve bunun olay yayılımıyla erişilebilirliğe ne yaptığını ele alır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.