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 tıklama etkinliği dinleyicileri çalışı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, sayfanın yanıt verme durumunu 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 kod 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. Terminalinizde depoyu 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ı: Duraklamayı 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ç de 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

Diğer Araçlar > Geliştirici Araçları menüsünden, sayfayı sağ tıklayıp İncele'yi seçerek veya klavye kısayolu kullanarak Geliştirici Araçları'nı açın.

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.

Sonuç olarak elde edilen zaman çizelgesinde bir Etkileşimler parçası bulunur. 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 pencereyi 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 işleyicisinin 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 izi

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ğırmalarının 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?

Herhangi bir fark olup olmadığını görmek için izleme yapıp etkileşimi incelemeyi deneyin.

Ayrı dinleyiciler

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

Tam kod: 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üncelleme

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 fazla sürdü.

Performans panelinde bir saniyelik etkileşim

Performans izleme: Ayrı işleyiciler

Tam kod: 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 izi: farklı etkinlik türleri

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

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

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

Bu örnekteki bir saniyelik etkileşime yakınlaştırılmış bir bakış. İş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 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. bileşen oluşturmayı tetikleyen bir 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 boyama işleminin tamamlanması için yavaş etkinlik dinleyicilerinin bitmesi bekleniyor olabilir.

7. Deneme: giriş gecikmesi

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

  • Yüklenmesi geciken bir <script> varsa ve bu öğe, yükleme sırasında sayfayı rastgele engelliyorsa.
  • 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örev sırasında gelen bir etkileşimi ve çoğunlukla giriş gecikmesine atfedilen 642 milisaniyelik bir etkileşimi gösteren DevTools 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 süren görevleri (veya işlenmesi uzun süren animasyon çerçevelerini) ve Total Blocking Time ölçmektir.

9. Sununun yavaş olması

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

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 açılıp kapatılması, ç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 yaygın olarak bulunan 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ğırmasına 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 blockFor(1000) içindeki requestAnimationFrame, sonraki boyamayı tam bir saniye boyunca engellemeye devam eder.

Performans panelinde bir saniyelik etkileşim

Ancak iki noktaya dikkat edin:

  • Fareyle üzerine geldiğinizde, etkinlik dinleyici 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, puanlar, zamanlayıcılar ve sayaç kullanıcı arayüzü ile duyarlılık son derece görseldir. Ancak ortalama bir sayfa test edilirken daha az belirgindir.

Etkileşimler uzun sürdüğünde bunun neden kaynaklandığı her zaman net değildir. Nedenler şunlar olabilir:

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

Duyarlılığı ölçmek için istediğiniz herhangi bir 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 son oluşturma işlemi oluşturulabildiğ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ün: 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ün: 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.

Kullanıcı arayüzü güncellendikten hemen sonra ana iş parçacığı kullanılabilir hale geldiğinden etkileşim artık kısa sürüyor. Uzun engelleme görevi çalışmaya devam eder ancak boyamadan bir süre sonra çalışır. Bu nedenle kullanıcı, kullanıcı arayüzünde 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 (bir sonraki boyamadan önce çalışmaya çalışır ve genellikle yine yavaş bir etkileşime neden olur) aksine, 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ün: timeout_100.html

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

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

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

Performans izi

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 süren görevler, yeni tıklamalarla çakıştığında etkinlik işleyici 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 işlemlerden 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 izi

Birden fazla etkileşim ancak tüm etkileşimler sonucunda tek bir uzun süren görev

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ı 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 süren görevin ortasına denk gelmesi ve giriş gecikmesi nedeniyle çok yavaş bir etkileşim haline gelmesi gibi talihsiz bir olasılık 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 uzun görevleri genellikle 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ı, setTimeout işine kıyasla gelen etkinlik dinleyicilerine ö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 süren 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ışmayı durdurur ve tarayıcı, gelen etkileşimlere yanıt verebilir. Ancak artık ayrı setTimeout'ler yerine tek bir await schedulerDotYield() 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 da 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ı düzenli olarak bırakırken yapılması gereken tüm işleri yapan blockInPiecesYieldyAborty for döngüsü başlatılı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ığı çalışması artık çok küçük parçalar halinde yapılıyor, etkileşimler kısa sürüyor ve çalışma 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 duyarlılığı gördüğü şekildir.
  • Giriş gecikmesi, etkinlik işleme süresi ve sunum gecikmesi etkileşim yanıt verme hızını etkiler.
  • INP'yi ve etkileşim dökümlerini Geliştirici Araçları ile kolayca ölçebilirsiniz.

Stratejiler

  • Sayfalarınızda uzun süre ç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