Sonraki Boyamayla Etkileşimi (INP) Anlama

1. Giriş

Interaction to Next Paint (INP) hakkında bilgi edinmek için etkileşimli bir demo ve codelab.

Ana iş parçacığındaki bir etkileşimi gösteren diyagram. Kullanıcı, görevlerin çalışmasını engellerken giriş yapar. Giriş, bu görevler tamamlanana kadar geciktirilir. Ardından pointerup, mouseup ve click etkinlik dinleyicileri çalıştırılır. Sonraki kare sunulana kadar oluşturma ve boyama işlemleri başlatılır.

Ön koşullar

  • HTML ve JavaScript geliştirme bilgisi
  • Önerilen: INP belgelerini okuyun.

Öğrenecekleriniz

  • Kullanıcı etkileşimlerinin ve bu etkileşimleri ele alma şeklinizin, sayfa yanıt verme hızını nasıl etkilediği.
  • Sorunsuz bir kullanıcı deneyimi için gecikmeler nasıl azaltılır ve ortadan kaldırılır?

İhtiyacınız olanlar

  • GitHub'dan kodu klonlayabilen ve npm komutlarını çalıştırabilen bir bilgisayar.
  • Metin düzenleyici
  • Tüm etkileşim ölçümlerinin çalışması için Chrome'un son sürümü.

2. Hazırlanın

Kodu alıp çalıştırma

Kod, web-vitals-codelabs deposunda bulunur.

  1. Depoyu terminalinizde klonlayın: git clone https://github.com/GoogleChromeLabs/web-vitals-codelabs.git
  2. Klonlanan dizine gidin: cd web-vitals-codelabs/understanding-inp
  3. Bağımlılıkları yükleyin: npm ci
  4. Web sunucusunu başlatın: npm run start
  5. Tarayıcınızda http://localhost:5173/understanding-inp/ adresini ziyaret edin.

Uygulamaya genel bakış

Sayfanın üst kısmında bir Puan sayacı ve Artır düğmesi bulunur. Reaktivite ve duyarlılıkla ilgili klasik bir demo!

Bu codelab'in demo uygulamasının ekran görüntüsü

Düğmenin altında dört ölçüm bulunur:

  • INP: Genellikle en kötü etkileşim olan mevcut INP puanı.
  • Etkileşim: En son etkileşimin puanı.
  • FPS: Sayfanın ana iş parçacığı saniyedeki kare sayısı.
  • Zamanlayıcı: Takılmayı görselleştirmeye yardımcı olan çalışan bir zamanlayıcı animasyonu.

FPS ve Zamanlayıcı girişleri, etkileşimleri ölçmek için hiç gerekli değildir. Bu öğeler, duyarlılığı görselleştirmeyi kolaylaştırmak için eklenir.

Deneyin

Artır düğmesiyle etkileşime geçmeyi deneyin ve puanın artmasını izleyin. INP ve Etkileşim değerleri her artışta değişiyor mu?

INP, kullanıcının etkileşimde bulunmasından sayfanın oluşturulan güncellemeyi kullanıcıya göstermesine kadar geçen süreyi ölçer.

3. Chrome Geliştirici Araçları ile etkileşimleri ölçme

Geliştirici Araçları'nı açmak için Diğer Araçlar > Geliştirici Araçları menüsünü kullanabilir, sayfayı sağ tıklayıp İncele'yi seçebilir veya klavye kısayolu kullanabilirsiniz.

Etkileşimleri ölçmek için kullanacağınız Performans paneline geçin.

Uygulamanın yanında Geliştirici Araçları Performans panelinin ekran görüntüsü

Ardından, Performans panelinde bir etkileşim yakalayın.

  1. Kaydet'e basın.
  2. Sayfayla etkileşimde bulunun (Artır düğmesine basın).
  3. Kaydı durdurun.

Oluşan zaman çizelgesinde Etkileşimler izini görürsünüz. Sol taraftaki üçgeni tıklayarak genişletin.

DevTools performans panelini kullanarak etkileşim kaydetme işleminin animasyonlu gösterimi

İki etkileşim gösterilir. Kaydırarak veya W tuşunu basılı tutarak ikinci ekrana yakınlaştırın.

Geliştirici Araçları'ndaki Performans panelinin ekran görüntüsü, imlecin paneldeki etkileşimin üzerinde durduğu ve etkileşimin kısa süresini listeleyen bir ipucu

Etkileşimin üzerine geldiğinizde, etkileşimin hızlı olduğunu, işleme süresinde zaman harcanmadığını ve giriş gecikmesi ile sunum gecikmesinde minimum süre harcandığını görebilirsiniz. Bu sürelerin tam uzunluğu, makinenizin hızına bağlıdır.

4. Uzun süren etkinlik işleyicileri

index.js dosyasını açın ve etkinlik dinleyicisinin içindeki blockFor işlevinin yorumunu kaldırın.

Tam kodu görmek için: click_block.html

button.addEventListener('click', () => {
  blockFor(1000);
  score.incrementAndUpdateUI();
});

Dosyayı kaydedin. Sunucu, değişikliği görür ve sayfayı sizin için yeniler.

Sayfayla tekrar etkileşim kurmayı deneyin. Etkileşimler artık belirgin şekilde daha yavaş olacak.

Performans izleme

Bunun nasıl göründüğünü görmek için performans panelinde başka bir kayıt alın.

Performans panelinde bir saniye süren etkileşim

Eskiden kısa süren etkileşimler artık tam bir saniye sürüyor.

Etkileşimin üzerine geldiğinizde, sürenin neredeyse tamamen "İşleme süresi" içinde harcandığını görürsünüz. Bu süre, etkinlik dinleyici geri çağırma işlemlerinin yürütülmesi için geçen süredir. Engelleme blockFor çağrısı tamamen etkinlik işleyicinin içinde olduğundan süre burada harcanır.

5. Deneme: işleme süresi

INP üzerindeki etkisini görmek için etkinlik işleyici çalışmasını yeniden düzenlemenin yollarını deneyin.

Önce kullanıcı arayüzünü güncelleyin

JavaScript çağrılarının sırasını değiştirirseniz (önce kullanıcı arayüzünü güncelleyip sonra engellerseniz) ne olur?

Tam kodu görüntüleyin: ui_first.html

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
  blockFor(1000);
});

Kullanıcı arayüzünün daha önce göründüğünü fark ettiniz mi? Sıra, INP puanlarını etkiler mi?

Farklılık olup olmadığını görmek için izleme yapıp etkileşimi incelemeyi deneyin.

Ayrı dinleyiciler

İşi ayrı bir etkinlik dinleyicisine taşırsanız ne olur? Kullanıcı arayüzünü bir etkinlik işleyicide güncelleyin ve sayfayı ayrı bir işleyiciden engelleyin.

Tam kodu görüntüleyin: two_click.html

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
});

button.addEventListener('click', () => {
  blockFor(1000);
});

Şimdi performans panelinde nasıl görünüyor?

Farklı etkinlik türleri

Çoğu etkileşim, işaretçi veya tuş etkinliklerinden imleçle üzerine gelme, odaklanma/bulanıklaştırma ve beforechange ile beforeinput gibi sentetik etkinliklere kadar birçok etkinlik türünü tetikler.

Birçok gerçek sayfada farklı etkinlikler için işleyiciler bulunur.

Etkinlik işleyicilerin etkinlik türlerini değiştirirseniz ne olur? Örneğin, click etkinlik işleyicilerinden birini pointerup veya mouseup ile mi değiştirmek istiyorsunuz?

Tam kodu görüntüleyin: diff_handlers.html

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
});

button.addEventListener('pointerup', () => {
  blockFor(1000);
});

Kullanıcı arayüzü güncellemesi yok

Kullanıcı arayüzünü güncelleme çağrısını etkinlik işleyiciden kaldırırsanız ne olur?

Tam kodu görüntüleyin: no_ui.html

button.addEventListener('click', () => {
  blockFor(1000);
  // score.incrementAndUpdateUI();
});

6. İşleme süresi deneme sonuçları

Performans izi: Önce kullanıcı arayüzünü güncelleyin

Tam kodu görüntüleyin: ui_first.html

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
  blockFor(1000);
});

Düğmeyi tıklama işleminin performans paneli kaydına baktığınızda sonuçların değişmediğini görebilirsiniz. Kullanıcı arayüzü güncellemesi, engelleme kodundan önce tetiklenmiş olsa da tarayıcı, etkinlik işleyici tamamlanana kadar ekrana çizilenleri güncellemedi. Bu nedenle, etkileşimin tamamlanması yine de bir saniyeden biraz daha uzun sürdü.

Performans panelinde bir saniyelik hareketsiz etkileşim

Performans izleme: Ayrı dinleyiciler

Tam kodu görüntüleyin: two_click.html

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
});

button.addEventListener('click', () => {
  blockFor(1000);
});

İşlevsel olarak yine bir fark yoktur. Etkileşim yine de tam bir saniye sürer.

Tıklama etkileşimini iyice yakınlaştırırsanız click etkinliği sonucunda gerçekten de iki farklı işlevin çağrıldığını görürsünüz.

Beklendiği gibi, ilk görev (kullanıcı arayüzünü güncelleme) inanılmaz derecede hızlı çalışırken ikinci görev bir saniye sürüyor. Ancak etkilerinin toplamı, son kullanıcı için aynı yavaş etkileşime neden olur.

Bu örnekteki bir saniyelik etkileşimin yakınlaştırılmış görünümü. İlk işlev çağrısının tamamlanmasının bir milisaniyeden kısa sürdüğü gösteriliyor.

Performans izleme: farklı etkinlik türleri

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
});

button.addEventListener('pointerup', () => {
  blockFor(1000);
});

Bu sonuçlar birbirine çok benziyor. Etkileşim yine bir saniye sürüyor. Tek fark, yalnızca kullanıcı arayüzü güncellemesi yapan daha kısa click dinleyicinin artık engelleme yapan pointerup dinleyiciden sonra çalışması.

Bu örnekteki bir saniyelik etkileşimin yakınlaştırılmış görünümü. İşaretçi yukarı etkinlik işleyicisi tamamlandıktan sonra tıklama etkinlik işleyicisinin tamamlanmasının bir milisaniyeden kısa sürdüğü gösteriliyor.

Performans izi: Kullanıcı arayüzü güncellemesi yok

Tam kodu görüntüleyin: no_ui.html

button.addEventListener('click', () => {
  blockFor(1000);
  // score.incrementAndUpdateUI();
});
  • Puan güncellenmez ancak sayfa güncellenmeye devam eder.
  • Animasyonlar, CSS efektleri, varsayılan web bileşeni işlemleri (form girişi), metin girişi ve metin vurgulama güncellenmeye devam eder.

Bu durumda düğme, tıklandığında etkin duruma geçer ve geri döner. Bu işlem için tarayıcı tarafından boyama yapılması gerekir. Bu da INP'nin devam ettiği anlamına gelir.

Etkinlik dinleyici, ana iş parçacığını bir saniye boyunca engelleyerek sayfanın boyanmasını önlediğinden etkileşim yine bir saniye sürüyor.

Performans paneli kaydı almak, önceki etkileşimlerle neredeyse aynı etkileşimi gösterir.

Performans panelinde bir saniyelik hareketsiz etkileşim

Önemli nokta

Herhangi bir etkinlik işleyicide çalışan herhangi bir kod, etkileşimi geciktirir.

  • Buna, farklı komut dosyalarından ve çerçevelerden veya dinleyicilerde çalışan kitaplık kodundan (ör. bir bileşenin oluşturulmasını tetikleyen durum güncellemesi) kaydedilen dinleyiciler dahildir.
  • Yalnızca kendi kodunuz değil, tüm üçüncü taraf komut dosyaları.

Bu yaygın bir sorundur.

Son olarak: Kodunuz boyama işlemini tetiklemiyor olsa bile yavaş etkinlik işleyicilerin tamamlanması için boyama işlemi bekliyor olabilir.

7. Deneme: giriş gecikmesi

Etkinlik işleyicilerinin dışındaki uzun süre çalışan kodlar ne olacak? Örneğin:

  • Yüklenmesi geciken ve yükleme sırasında sayfayı rastgele engelleyen bir <script> varsa.
  • Sayfayı düzenli olarak engelleyen setInterval gibi bir API çağrısı mı?

blockFor öğesini etkinlik işleyiciden kaldırıp setInterval() öğesine eklemeyi deneyin:

Tam kodu görüntüleyin: input_delay.html

setInterval(() => {
  blockFor(1000);
}, 3000);


button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
});

Süreç

8. Giriş gecikmesi deneme sonuçları

Tam kodu görüntüleyin: input_delay.html

setInterval(() => {
  blockFor(1000);
}, 3000);


button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
});

setInterval engelleme görevi çalışırken gerçekleşen bir düğme tıklamasının kaydedilmesi, etkileşimde herhangi bir engelleme işlemi yapılmamış olsa bile uzun süren bir etkileşime neden olur.

Bu uzun süreli dönemlere genellikle uzun görevler denir.

Geliştirici Araçları'nda etkileşimin üzerine geldiğinizde, etkileşim süresinin artık öncelikle işleme süresine değil, giriş gecikmesine atfedildiğini görebilirsiniz.

Bir saniyelik engelleme görevini, bu görevin ortasında gelen bir etkileşimi ve çoğunlukla giriş gecikmesinden kaynaklanan 642 milisaniyelik bir etkileşimi gösteren Geliştirici Araçları Performans paneli

Bu durumun etkileşimleri her zaman etkilemediğini unutmayın. Görev çalışırken tıklamazsanız şanslı olabilirsiniz. Bu tür "rastgele" hapşırmalar, yalnızca bazen sorunlara neden olduklarında hata ayıklama açısından kabus olabilir.

Bunları izlemenin bir yolu, uzun görevleri (veya uzun animasyon karelerini) ve toplam engelleme süresini ölçmektir.

9. Sunumun yavaş olması

Şimdiye kadar giriş gecikmesi veya etkinlik dinleyicileri aracılığıyla JavaScript'in performansına baktık. Ancak sonraki boyamayı etkileyen başka neler var?

Sayfayı pahalı efektlerle güncellemek!

Sayfa güncellemesi hızlı bir şekilde yapılsa bile tarayıcının bunları oluşturmak için çok çalışması gerekebilir.

Ana iş parçacığında:

  • Durum değişikliklerinden sonra güncellemelerin oluşturulması gereken kullanıcı arayüzü çerçeveleri
  • DOM değişiklikleri veya çok sayıda maliyetli CSS sorgu seçicinin etkinleştirilmesi, çok sayıda stil, düzen ve boyama işlemini tetikleyebilir.

Ana ileti dizisi dışında:

  • GPU efektlerini desteklemek için CSS kullanma
  • Çok büyük ve yüksek çözünürlüklü resimler ekleme
  • Karmaşık sahneler çizmek için SVG/Canvas kullanma

Web&#39;de oluşturmanın farklı öğelerinin taslağı

RenderingNG

Web'de sıkça rastlanan bazı örnekler:

  • Bir bağlantı tıklandıktan sonra ilk görsel geri bildirimi sağlamak için duraklamadan DOM'un tamamını yeniden oluşturan bir SPA sitesi.
  • Dinamik kullanıcı arayüzüyle karmaşık arama filtreleri sunan ancak bunu yapmak için pahalı dinleyiciler çalıştıran bir arama sayfası.
  • Tüm sayfanın stilini/düzenini tetikleyen bir koyu mod açma/kapatma düğmesi

10. Deneme: sunum gecikmesi

requestAnimationFrame yavaş

requestAnimationFrame() API'sini kullanarak uzun bir sunum gecikmesini simüle edelim.

blockFor çağrısını, etkinlik işleyici döndükten sonra çalışacak şekilde bir requestAnimationFrame geri çağırma işlevine taşıyın:

Tam kodu görüntüleyin: presentation_delay.html

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
  requestAnimationFrame(() => {
    blockFor(1000);
  });
});

Süreç

11. Sunum gecikmesi denemesi sonuçları

Tam kodu görüntüleyin: presentation_delay.html

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
  requestAnimationFrame(() => {
    blockFor(1000);
  });
});

Etkileşim bir saniye sürüyor. Peki ne oldu?

requestAnimationFrame, bir sonraki boyamadan önce geri aranmayı istiyor. INP, etkileşimden sonraki boyamaya kadar geçen süreyi ölçtüğünden requestAnimationFrame içindeki blockFor(1000), sonraki boyamayı tam bir saniye boyunca engellemeye devam eder.

Performans panelinde bir saniyelik hareketsiz etkileşim

Ancak iki noktaya dikkat edin:

  • Fareyle üzerine gelindiğinde, etkinlik işleyici döndükten sonra ana iş parçacığı engellemesi gerçekleştiği için tüm etkileşim süresinin artık "sunum gecikmesi" içinde harcandığını görürsünüz.
  • Ana iş parçacığı etkinliğinin kökü artık tıklama etkinliği değil, "Animation Frame Fired" (Animasyon Karesi Tetiklendi) etkinliğidir.

12. Etkileşimleri teşhis etme

Bu test sayfasında duyarlılık, puanlar, zamanlayıcılar ve sayaç kullanıcı arayüzüyle birlikte oldukça görseldir. Ancak ortalama bir sayfa test edilirken daha az belirgindir.

Etkileşimler uzun sürdüğünde bunun nedenini belirlemek her zaman kolay olmaz. Bunun nedeni aşağıdakilerden hangisi olabilir?

  • Giriş gecikmesi mi var?
  • Etkinlik işleme süresi?
  • Yanıt sunumu gecikmesi?

Duyarlılığı ölçmek için istediğiniz sayfada Geliştirici Araçları'nı kullanabilirsiniz. Bu alışkanlığı kazanmak için aşağıdaki akışı deneyin:

  1. Web'de normalde yaptığınız gibi gezinin.
  2. Geliştirici Araçları Performans panelinin canlı metrikler görünümündeki Etkileşimler günlüğünü takip edin.
  3. Performansı düşük bir etkileşim görürseniz bunu tekrarlamayı deneyin:
  • Tekrar edemiyorsanız analiz almak için etkileşim günlüğünü kullanın.
  • Tekrarlayabiliyorsanız Performans panelinde bir iz kaydı yapın.

Tüm gecikmeler

Sayfaya bu sorunların hepsinden biraz eklemeyi deneyin:

Tam kodu görüntüleyin: all_the_things.html

setInterval(() => {
  blockFor(1000);
}, 3000);

button.addEventListener('click', () => {
  blockFor(1000);
  score.incrementAndUpdateUI();

  requestAnimationFrame(() => {
    blockFor(1000);
  });
});

Ardından, sorunları teşhis etmek için konsol ve performans panelini kullanın.

13. Deneme: Eş zamansız çalışma

Ağ istekleri gönderme, zamanlayıcı başlatma veya yalnızca genel durumu güncelleme gibi etkileşimlerde görsel olmayan efektleri başlatabildiğiniz için bu efektler sonunda sayfayı güncellediğinde ne olur?

Tarayıcı, yeni bir oluşturma güncellemesine gerek olmadığına karar verse bile, etkileşimden sonraki sonraki boyama oluşturulmasına izin verildiği sürece etkileşim ölçümü durdurulur.

Bunu denemek için kullanıcı arayüzünü tıklama işleyicisinden güncellemeye devam edin ancak engelleme işlemini zaman aşımından çalıştırın.

Tam kodu görüntüleyin: timeout_100.html

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();

  setTimeout(() => {
    blockFor(1000);
  }, 100);
});

Şimdi ne olacak?

14. Eş zamansız çalışma denemesi sonuçları

Tam kodu görüntüleyin: timeout_100.html

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();

  setTimeout(() => {
    blockFor(1000);
  }, 100);
});

Bir saniye süren bir görevle 27 milisaniyelik etkileşim artık izlemede daha sonra gerçekleşiyor.

Ana iş parçacığı, kullanıcı arayüzü güncellendikten hemen sonra kullanılabilir olduğundan etkileşim artık kısa sürüyor. Uzun engelleme görevi çalışmaya devam eder ancak boyamadan sonra çalışır. Bu nedenle kullanıcı, kullanıcı arayüzüyle ilgili anında geri bildirim alır.

Ders: Kaldıramıyorsanız en azından taşıyın.

Yöntemler

Sabit 100 milisaniyelik setTimeout değerinden daha iyisini yapabilir miyiz? Muhtemelen kodun mümkün olduğunca hızlı çalışmasını istiyoruz. Aksi takdirde kodu kaldırmamız gerekirdi.

Hedef:

  • Etkileşim incrementAndUpdateUI() sürecek.
  • blockFor() mümkün olan en kısa sürede çalışır ancak sonraki boyamayı engellemez.
  • Bu sayede, "sihirli zaman aşımları" olmadan öngörülebilir davranışlar elde edilir.

Bunu yapmanın bazı yolları şunlardır:

  • setTimeout(0)
  • Promise.then()
  • requestAnimationFrame
  • requestIdleCallback
  • scheduler.postTask()

"requestPostAnimationFrame"

Tek başına requestAnimationFrame'dan (bir sonraki boyamadan önce çalışmaya çalışır ve genellikle yine yavaş bir etkileşime neden olur) farklı olarak requestAnimationFrame + setTimeout, requestPostAnimationFrame için basit bir polyfill oluşturur ve geri çağırmayı bir sonraki boyamadan sonra çalıştırır.

Tam kodu görüntüleyin: raf+task.html

function afterNextPaint(callback) {
  requestAnimationFrame(() => {
    setTimeout(callback, 0);
  });
}

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();

  afterNextPaint(() => {
    blockFor(1000);
  });
});

Ergonomi için bunu bir sözle bile sarmalayabilirsiniz:

Tam kodu görüntüleyin: raf+task2.html

async function nextPaint() {
  return new Promise(resolve => afterNextPaint(resolve));
}

button.addEventListener('click', async () => {
  score.incrementAndUpdateUI();

  await nextPaint();
  blockFor(1000);
});

15. Birden fazla etkileşim (ve öfke tıklamaları)

Uzun süren engelleme işlemlerini başka bir yere taşımak yardımcı olabilir ancak bu uzun görevler yine de sayfayı engeller ve gelecekteki etkileşimlerin yanı sıra diğer birçok sayfa animasyonunu ve güncellemesini etkiler.

Sayfanın eşzamansız engelleme çalışması sürümünü tekrar deneyin (veya son adımda çalışmayı ertelemeyle ilgili kendi varyasyonunuzu oluşturduysanız kendi sürümünüzü deneyin):

Tam kodu görüntüleyin: timeout_100.html

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();

  setTimeout(() => {
    blockFor(1000);
  }, 100);
});

Hızlıca birden fazla kez tıklarsanız ne olur?

Performans izleme

Her tıklama için bir saniyelik bir görev sıraya alınır. Bu da ana iş parçacığının önemli bir süre boyunca engellenmesini sağlar.

Ana iş parçacığında birden fazla saniye süren görevler, etkileşimlerin 800 ms kadar yavaş olmasına neden oluyor

Bu uzun görevler, yeni tıklamalarla çakıştığında etkinlik dinleyici neredeyse anında yanıt vermesine rağmen etkileşimler yavaşlar. Giriş gecikmeleriyle ilgili önceki denemedekiyle aynı durumu oluşturduk. Bu sefer giriş gecikmesi setInterval'dan değil, önceki etkinlik dinleyicileri tarafından tetiklenen çalışmalardan kaynaklanıyor.

Stratejiler

İdeal olarak, uzun görevleri tamamen kaldırmak istiyoruz.

  • Gereksiz kodları (özellikle komut dosyalarını) tamamen kaldırın.
  • Uzun görevlerin çalışmasını önlemek için kodu optimize edin.
  • Yeni etkileşimler geldiğinde eski işi iptal edin.

16. 1. strateji: Debounce

Klasik bir strateji. Etkileşimler hızlı bir şekilde geldiğinde ve işleme ya da ağ etkileri çok fazla kaynak gerektirdiğinde, iptal edip yeniden başlatabilmek için işe başlamayı kasıtlı olarak geciktirin. Bu kalıp, otomatik tamamlama alanları gibi kullanıcı arayüzleri için yararlıdır.

  • setTimeout kullanarak pahalı işlerin başlatılmasını geciktirin. Zamanlayıcıyı 500 ila 1.000 milisaniye olarak ayarlayabilirsiniz.
  • Bu işlemi yaparken zamanlayıcı kimliğini kaydedin.
  • Yeni bir etkileşim gelirse clearTimeout kullanarak önceki zamanlayıcıyı iptal edin.

Tam kodu göster: debounce.html

let timer;
button.addEventListener('click', () => {
  score.incrementAndUpdateUI();

  if (timer) {
    clearTimeout(timer);
  }
  timer = setTimeout(() => {
    blockFor(1000);
  }, 1000);
});

Performans izleme

Birden fazla etkileşim ancak tüm etkileşimler sonucunda tek bir uzun iş görevi

Birden fazla tıklamaya rağmen yalnızca bir blockFor görevi çalıştırılır. Bu görev, çalıştırılmadan önce bir saniye boyunca tıklama olmamasını bekler. Metin girişine yazma veya birden fazla hızlı tıklama alması beklenen öğe hedefleri gibi aralıklı olarak gelen etkileşimler için bu, varsayılan olarak kullanılması gereken ideal bir stratejidir.

17. 2. strateji: Uzun süren çalışmaları kesintiye uğratma

Yine de, geri sıçrama süresi geçtikten hemen sonra başka bir tıklama gelmesi, bu uzun görevin ortasına denk gelmesi ve giriş gecikmesi nedeniyle çok yavaş bir etkileşim haline gelmesi gibi talihsiz bir ihtimal vardır.

İdeal olarak, görevimizin ortasında bir etkileşim geldiğinde yoğun işimize ara vermek isteriz. Böylece yeni etkileşimler hemen ele alınır. Bunu nasıl yapabiliriz?

isInputPending gibi bazı API'ler vardır ancak genel olarak uzun görevleri parçalara ayırmak daha iyidir.

Çok sayıda setTimeout

İlk deneme: Basit bir şey yapın.

Tam kodu görüntüleyin: small_tasks.html

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();

  requestAnimationFrame(() => {
    setTimeout(() => blockFor(100), 0);
    setTimeout(() => blockFor(100), 0);
    setTimeout(() => blockFor(100), 0);
    setTimeout(() => blockFor(100), 0);
    setTimeout(() => blockFor(100), 0);
    setTimeout(() => blockFor(100), 0);
    setTimeout(() => blockFor(100), 0);
    setTimeout(() => blockFor(100), 0);
    setTimeout(() => blockFor(100), 0);
    setTimeout(() => blockFor(100), 0);
  });
});

Bu, tarayıcının her görevi ayrı ayrı planlamasına olanak tanır ve giriş daha yüksek önceliğe sahip olabilir.

Birden fazla etkileşim var ancak planlanan tüm çalışmalar daha küçük birçok göreve bölünmüş durumda

Beş tıklama için beş saniyelik tam bir işe geri döndük ancak tıklama başına bir saniyelik her görev, 100 milisaniyelik on göreve ayrıldı. Sonuç olarak, bu görevlerle çakışan birden fazla etkileşim olsa bile hiçbir etkileşimde 100 milisaniyeden fazla giriş gecikmesi olmaz. Tarayıcı, gelen etkinlik dinleyicilerine setTimeout işine göre öncelik verir ve etkileşimler yanıt vermeye devam eder.

Bu strateji, özellikle ayrı giriş noktaları planlarken (ör. uygulama yükleme sırasında çağırmanız gereken bir dizi bağımsız özelliğiniz varsa) iyi sonuç verir. Yalnızca komut dosyalarını yüklemek ve her şeyi komut dosyası değerlendirme zamanında çalıştırmak, varsayılan olarak her şeyi devasa bir uzun görevde çalıştırabilir.

Ancak bu strateji, paylaşılan durumu kullanan bir for döngüsü gibi sıkıca bağlı kodları ayırmak için iyi çalışmaz.

Artık yield() ile

Ancak herhangi bir JavaScript işlevine kolayca "getiri noktaları" eklemek için modern async ve await'den yararlanabiliriz.

Örneğin:

Tam kodu görüntüleyin: yieldy.html

// Polyfill for scheduler.yield()
async function schedulerDotYield() {
  return new Promise(resolve => {
    setTimeout(resolve, 0);
  });
}

async function blockInPiecesYieldy(ms) {
  const ms_per_part = 10;
  const parts = ms / ms_per_part;
  for (let i = 0; i < parts; i++) {
    await schedulerDotYield();

    blockFor(ms_per_part);
  }
}

button.addEventListener('click', async () => {
  score.incrementAndUpdateUI();
  await blockInPiecesYieldy(1000);
});

Daha önce olduğu gibi, ana iş parçacığı bir iş parçası tamamlandıktan sonra çalışmaya devam eder ve tarayıcı, gelen etkileşimlere yanıt verebilir. Ancak artık ayrı setTimeout'ler yerine tek bir await schedulerDotYield() kullanılması yeterlidir. Bu da for döngüsünün ortasında bile kullanılabilecek kadar ergonomik olmasını sağlar.

Artık AbortContoller() ile

Bu yöntem işe yarasa da yeni etkileşimler gelip yapılması gereken işi değiştirmiş olsa bile her etkileşim daha fazla iş planlıyor.

Sıçrama önleme stratejisiyle, her yeni etkileşimde önceki zaman aşımını iptal ettik. Burada benzer bir şey yapabilir miyiz? Bunu yapmanın bir yolu AbortController() kullanmaktır:

Tam kodu görüntüleyin: aborty.html

// Polyfill for scheduler.yield()
async function schedulerDotYield() {
  return new Promise(resolve => {
    setTimeout(resolve, 0);
  });
}

async function blockInPiecesYieldyAborty(ms, signal) {
  const parts = ms / 10;
  for (let i = 0; i < parts; i++) {
    // If AbortController has been asked to stop, abandon the current loop.
    if (signal.aborted) return;

    await schedulerDotYield();

    blockFor(10);
  }
}

let abortController = new AbortController();

button.addEventListener('click', async () => {
  score.incrementAndUpdateUI();

  abortController.abort();
  abortController = new AbortController();

  await blockInPiecesYieldyAborty(1000, abortController.signal);
});

Bir tıklama geldiğinde, tarayıcının yeni etkileşimlere yanıt vermeye devam etmesi için ana iş parçacığını periyodik olarak bırakırken yapılması gereken tüm işleri yapan blockInPiecesYieldyAborty for döngüsünü başlatır.

İkinci bir tıklama geldiğinde ilk döngü AbortController ile iptal edildi olarak işaretlenir ve yeni bir blockInPiecesYieldyAborty döngüsü başlatılır. İlk döngü bir sonraki çalıştırma zamanında signal.aborted'nin artık true olduğunu fark eder ve başka işlem yapmadan hemen geri döner.

Ana iş parçacığı artık birçok küçük parçaya bölünmüş durumda. Etkileşimler kısa ve yalnızca gerektiği kadar sürüyor.

18. Sonuç

Tüm uzun görevleri bölmek, sitenin yeni etkileşimlere yanıt vermesini sağlar. Bu sayede hızlıca ilk geri bildiriminizi verebilir ve devam eden işi iptal etme gibi kararlar alabilirsiniz. Bazen bu, giriş noktalarını ayrı görevler olarak planlamak anlamına gelir. Bazen bu, uygun yerlere "verim" noktaları eklemek anlamına gelir.

Unutmayın

  • INP, tüm etkileşimleri ölçer.
  • Her etkileşim, girişten sonraki boyamaya kadar ölçülür. Bu, kullanıcının yanıt verme hızını gördüğü şekildir.
  • Giriş gecikmesi, etkinlik işleme süresi ve sunum gecikmesi etkileşim yanıt verme durumunu etkiler.
  • INP'yi ve etkileşim dökümlerini Geliştirici Araçları ile kolayca ölçebilirsiniz.

Stratejiler

  • Sayfalarınızda uzun süredir çalışan kodlar (uzun görevler) olmamalıdır.
  • Gereksiz kodu, bir sonraki boyama işlemine kadar etkinlik işleyicilerden çıkarın.
  • Oluşturma güncellemesinin tarayıcı için verimli olduğundan emin olun.

Daha fazla bilgi