Sonraki Boyamayla Etkileşimi (INP) Anlama

1. Giriş

Sonraki Boyamayla Etkileşim (INP) hakkında bilgi edinmek için etkileşimli bir demo ve codelab.

Ana iş parçacığındaki etkileşimi gösteren bir diyagram. Kullanıcı, görevlerin çalışmasını engellerken giriş yapar. Giriş, bu görevler tamamlanana kadar ertelenir, ardından işaretçi, fareyle üzerine gelme ve tıklama etkinlik işleyicileri çalışır ve ardından oluşturma ve boyama işi bir sonraki kare sunulana kadar başlatılır.

Ön koşullar

  • HTML ve JavaScript geliştirme hakkında bilgi sahibi olmak.
  • Önerilen: INP dokümanlarını okuyun.

Öğrenecekleriniz

  • Kullanıcı etkileşimlerinin karşılıklı etkileşimi ve bu etkileşimleri ele alma biçiminizin sayfa yanıt verme üzerindeki etkisi.
  • 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ışabilmesi için Chrome'un güncel bir sürümü.

2. Hazırlanın

Kodu alma ve çalıştırma

Kod, web-vitals-codelabs deposunda bulunur.

  1. Terminalinizdeki depoyu klonlayın: git clone https://github.com/GoogleChromeLabs/web-vitals-codelabs.git
  2. Klonlanan dizine geçiş: 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. Tepki verme ve duyarlılık konusunda klasik bir demo

Bu codelab için demo uygulamanın ekran görüntüsü

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

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

Etkileşimleri ölçmek için FPS ve Zamanlayıcı girişleri gerekli değildir. Bunlar, duyarlılığı görselleştirmeyi biraz kolaylaştırmak amacıyla eklenmiştir.

Deneyin

Artır düğmesiyle etkileşimde bulunmaya çalışın ve puan artışını izleyin. INP ve Etkileşim değerleri her artımla değişiyor mu?

INP, kullanıcının etkileşimde bulunduğu andan sayfanın oluşturulan güncellemeyi kullanıcıya gerçekten göstermesine kadar geçen süreyi ölçer.

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

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

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

Uygulamanın yanındaki 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.

Ortaya çıkan zaman çizelgesinde bir Etkileşimler kanalı görürsünüz. Sol taraftaki üçgeni tıklayarak bu segmenti genişletin.

Geliştirici Araçları performans panelini kullanarak bir etkileşimi kaydetmenin animasyonlu gösterimi

İki etkileşim görünür. İkincisinde ise W tuşunu kaydırarak veya basılı tutarak yakınlaştırın.

Geliştirici Araçları performans panelinin ekran görüntüsü, imleç paneldeki etkileşimin üzerinde duruyor ve etkileşimin kısa zamanlamasını listeleyen ipucu

Etkileşimin üzerine geldiğinizde, etkileşimin hızlı olduğunu, işleme süresinde hiç zaman harcamadığını ve giriş gecikmesi ile sunum gecikmesi'nde minimum süreyi görebilirsiniz. Bu sürenin tam uzunluğu makinenizin hızına bağlıdır.

4. Köklü etkinlik işleyiciler

index.js dosyasını açın ve etkinlik işleyicideki blockFor işlevinin açıklamasını kaldırın.

Kodun tamamını görün: 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şimde bulunmayı deneyin. Etkileşimler önemli ölçüde yavaşlar.

Performans izleme

Performans panelinde başka bir kayıt alarak bunun orada nasıl göründüğünü inceleyin.

Performans panelinde bir saniyelik etkileşim

Eskiden kısa bir etkileşim şimdi tam bir saniye sürüyor.

Etkileşimin üzerine geldiğinizde, zamanın neredeyse tamamen "İşleme süresi"nde harcandığına dikkat edin. Bu, etkinlik işleyici geri çağırmalarının yürütülmesi için harcanan süredir. Engelleyen blockFor çağrısı tamamen etkinlik işleyici içinde olduğundan zaman bu şekildedir.

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

js çağrılarının sırasını değiştirirseniz ne olur? Önce kullanıcı arayüzünü güncelleyip ardından engellerseniz ne olur?

Kodun tamamını inceleyin: 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? Sipariş, INP puanlarını etkiler mi?

Herhangi bir fark olup olmadığını görmek için bir iz almayı ve etkileşimi incelemeyi deneyin.

Ayrı dinleyiciler

Çalışmayı ayrı bir etkinlik işleyiciye taşırsanız ne olur? Bir etkinlik işleyicide kullanıcı arayüzünü güncelleyin ve sayfayı ayrı bir işleyiciden engelleyin.

Tam kodu inceleyin: Two_click.html

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

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

Bu sekme artık performans panelinde nasıl görünüyor?

Farklı etkinlik türleri

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

Birçok gerçek sayfanın birçok farklı etkinliği işleyicisi vardır.

Etkinlik işleyicileri için etkinlik türlerini değiştirirseniz ne olur? Örneğin, click etkinlik işleyicilerinden biri pointerup veya mouseup ile değiştirilsin mi?

Kodun tamamını inceleyin: diff_handlers.html

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

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

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

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

Kodun tamamını inceleyin: no_ui.html

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

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

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

Kodun tamamını inceleyin: ui_first.html

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

Düğmeyi tıklamanın Performans paneli kaydına baktığınızda sonuçların değişmediğini görebilirsiniz. Engelleme kodundan önce bir kullanıcı arayüzü güncellemesi tetiklenmiş olsa da tarayıcı, ekranda gösterilen içeriği etkinlik işleyici tamamlanana kadar güncellememiştir. Bu, etkileşimin tamamlanmasının bir saniyeden biraz uzun sürdüğü anlamına gelir.

Performans panelinde hâlâ bir saniyelik uzunlukta bir etkileşim

Performans izleme: ayrı dinleyiciler

Tam kodu inceleyin: Two_click.html

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

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

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

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

Beklendiği gibi, ilki (kullanıcı arayüzü güncellemesi) inanılmaz derecede hızlı çalışırken, ikincisi tam saniye sürer. Ancak bunların etkilerinin toplamı son kullanıcı ile aynı yavaş etkileşime neden olur.

Bu örnekteki bir saniyelik uzun etkileşime yakın bir bakış. Tamamlanması bir milisaniyeden kısa süren ilk işlev çağrısı

Performans izleme: farklı etkinlik türleri

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

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

Bu sonuçlar çok benzerdir. Etkileşim hâlâ bir saniye sürer; tek fark, yalnızca kullanıcı arayüzü güncellemesi olan daha kısa click işleyicisinin artık engelleme yapan pointerup işleyicisinden sonra çalışmasıdır.

Bu örnekte, işaretçi işleyiciden sonra tıklama etkinliği işleyicisinin tamamlamasının bir milisaniyeden kısa sürdüğünü gösteren bir saniyelik uzun etkileşime yakınlaştırılmış bir görüntü

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

Kodun tamamını inceleyin: no_ui.html

button.addEventListener('click', () => {
  blockFor(1000);
  // score.incrementAndUpdateUI();
});
  • Puan güncellenmez ancak sayfa yine de güncellenir.
  • Animasyonlar, CSS efektleri, varsayılan web bileşeni işlemleri (form girişi), metin girişi, vurgulu metinlerin tümü güncellenmeye devam ediyor.

Bu durumda düğme etkin duruma gelir ve tıklandığında geri döner. Bu durumda, tarayıcının boyaması gerekir. Bu da hâlâ bir INP olduğu anlamına gelir.

Etkinlik işleyici, ana iş parçacığını bir saniyeliğine engellediği için sayfanın boyanmasını engellediğinden etkileşim yine de tam bir saniye sürer.

Performans paneli kaydı yapıldığında, etkileşim öncekiyle neredeyse aynı şekilde gösterilir.

Performans panelinde hâlâ bir saniyelik uzunlukta bir etkileşim

Paket servis

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

  • Bu, bileşen oluşturmayı tetikleyen bir durum güncellemesi gibi farklı komut dosyalarından ve işleyicilerde çalışan çerçeve veya kitaplık kodlarından kaydedilen işleyicileri içerir.
  • Yalnızca kendi kodunuzu değil, tüm üçüncü taraf komut dosyalarını da kullanabilirsiniz.

Bu yaygın bir sorundur!

Son olarak: Kodunuzun bir boyamayı tetiklememesi, bir boyamanın yavaş etkinlik işleyicilerin tamamlamasını beklemediği anlamına gelmez.

7. Deneme: Giriş gecikmesi

Etkinlik işleyiciler dışında uzun süre çalışan kod ne olacak? Örneğin:

  • Yükleme sırasında sayfayı rastgele engelleyen bir geç yüklenen <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:

Kodun tamamını görün: giriş_delay.html

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


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

Süreç

8. Giriş gecikmesi deneme sonuçları

Kodun tamamını görün: giriş_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şimin kendisinde herhangi bir engelleme işlemi yapılmasa bile uzun süreli bir etkileşimle sonuçlanır.

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

Fareyle Geliştirici Araçları'ndaki etkileşimin üzerine geldiğinizde, etkileşim süresinin artık işleme süresiyle değil, giriş gecikmesiyle ilişkilendirildiğini görebilirsiniz.

Çoğunlukla giriş gecikmesiyle ilişkilendirilen 642 milisaniyelik etkileşimi, bir saniyelik engelleme görevini ve bu görevin kısmen öncesindeki bir etkileşimi gösteren Geliştirici Araçları Performans paneli

Unutmayın, bu durum etkileşimleri her zaman etkilemez. Görev çalışırken tıklamazsanız şansınızı kaybedebilirsiniz. Böyle bir "rastgele" hapşırma, ancak bazen sorunlara yol açtığında hata ayıklamak için bir kabus olabilir.

Bunları tespit etmenin yollarından biri, uzun görevleri (veya Uzun Animasyon Karelerini) ve Toplam Engelleme Süresi'ni ölçmektir.

9. Sunu yavaş

Şimdiye kadar, giriş gecikmesi veya etkinlik işleyiciler yoluyla JavaScript'in performansını inceledik, ancak bir sonraki boyama işlemini başka neler etkiler?

Sayfayı da pahalı efektlerle güncellemek.

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

Ana ileti dizisinde:

  • Durum değişikliklerinden sonra güncellemeleri oluşturması gereken kullanıcı arayüzü çerçeveleri
  • DOM değişiklikleri veya çok sayıda pahalı CSS sorgu seçicisinin açılıp kapatılması birçok Stil, Düzen ve Boyama işlemini tetikleyebilir.

Ana ileti dizisinden:

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

Web&#39;de oluşturma işleminin farklı öğelerini gösteren çizim

RenderingNG

Web'de yaygın olarak bulunan bazı örnekler:

  • Başlangıçtaki görsel geri bildirim sağlamak için duraklamadan, bir bağlantıyı tıkladıktan sonra DOM'nin tamamını yeniden oluşturan bir SPA sitesi.
  • Dinamik bir kullanıcı arayüzüne sahip karmaşık arama filtreleri sunan ancak bunun için pahalı işleyiciler çalıştıran bir arama sayfası.
  • Tüm sayfada stili/düzeni tetikleyen koyu mod açma/kapatma düğmesi

10. Deneme: Sunu gecikmesi

requestAnimationFrame yavaş

Şimdi, requestAnimationFrame() API'yi kullanarak uzun bir sunum gecikmesi simülasyonu yapalım.

Etkinlik işleyici şunu döndürdükten sonra çalışması için blockFor çağrısını bir requestAnimationFrame geri çağırmasına taşıyın:

Kodun tamamını görün: Presentation_delay.html

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

Süreç

11. Sunum gecikmesi deneme sonuçları

Kodun tamamını görün: Presentation_delay.html

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

Etkileşim bir saniye kadar uzun kalıyor. Peki ne oldu?

requestAnimationFrame, bir sonraki boyamadan önce geri aranma isteğinde bulunuyor. INP, etkileşimden bir sonraki boyaya kadar geçen süreyi ölçtüğünden, requestAnimationFrame içindeki blockFor(1000) bir sonraki boyayı tam saniye boyunca engellemeye devam eder.

Performans panelinde hâlâ bir saniyelik uzunlukta bir etkileşim

Ancak dikkat edilmesi gereken iki nokta vardır:

  • Fareyle üzerine geldiğinizde, tüm etkileşim süresinin artık "sunum gecikmesi"nde harcandığını görürsünüz etkinlik işleyici geri döndükten sonra gerçekleştiği için ana iş parçacığı engellemesi gerçekleşir.
  • Ana iş parçacığı etkinliğinin kökü artık tıklama etkinliği değil, "Animasyon Çerçevesi Tetiklendi"dir.

12. Etkileşimleri teşhis etme

Bu test sayfasında puanlar, kronometreler ve sayaç kullanıcı arayüzüyle duyarlılık son derece görseldir. Ancak ortalama bir sayfayı test ederken bu durum daha belirsizdir.

Etkileşimler uzun sürerse nedenin ne olduğu her zaman net olarak anlaşılmayabilir. Hedef:

  • Giriş gecikmesi
  • Etkinlik işleme süresi
  • Sunu gecikmesi?

İstediğiniz sayfada, duyarlılığı ölçmenize yardımcı olması için Geliştirici Araçları'nı kullanabilirsiniz. Bu alışkanlığı kazanmak için şu akışı deneyin:

  1. Her zamanki gibi web'de gezinin.
  2. İsteğe bağlı: Web Vitals uzantısı etkileşimleri kaydederken Geliştirici Araçları konsolunu açık bırakın.
  3. Düşük performans gösteren bir etkileşimle karşılaşırsanız etkileşimi tekrarlamayı deneyin:
  • İşlemi tekrarlayamıyorsanız analizler almak için konsol günlüklerini kullanabilirsiniz.
  • Tekrar edebiliyorsanız performans panelinde kaydedin.

Tüm gecikmeler

Sayfaya tüm şu sorunlardan birkaçını eklemeyi deneyin:

Kodun tamamını görün: 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 konsolu ve performans panelini kullanın.

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

Etkileşimlerin içinde ağ isteklerinde bulunma, zamanlayıcı başlatma veya yalnızca genel durumu güncelleme gibi görsel olmayan efektler başlatabildiğiniz için bunlar sonuçta sayfayı güncellediğinde ne olur?

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

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

Kodun tamamını inceleyin: zaman aşımı_100.html

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

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

Şimdi ne olacak?

14. Eş zamansız iş denemesi sonuçları

Kodun tamamını inceleyin: zaman aşımı_100.html

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

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

Artık izin ilerleyen aşamalarında bir saniyelik görevle 27 milisaniyelik etkileşim

Ana iş parçacığı, kullanıcı arayüzü güncellendikten hemen sonra kullanılabilir hâle geldiğinden etkileşim şu anda kısadır. Uzun engelleme görevi hâlâ çalışıyor, yalnızca boyama işleminden bir süre sonra çalışıyor. Bu nedenle, kullanıcı anında kullanıcı arayüzü geri bildirimi alır.

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

Yöntemler

100 milisaniyelik sabit bir setTimeout değerinden daha iyi olabilir mi? Muhtemelen kodun mümkün olduğunca hızlı çalışmasını istiyoruz, aksi takdirde kodu kaldıracaktınız.

Hedef:

  • Etkileşim incrementAndUpdateUI() çalıştıracak.
  • blockFor() en kısa sürede çalışır ancak bir sonraki boyamayı engellemez.
  • Bu, "sihirli zaman aşımları" olmadan tahmin edilebilir davranışlar sağlar.

Bunu başarmanın bazı yolları şunlardır:

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

&quot;requestPostAnimationFrame&quot;

requestAnimationFrame + setTimeout, bir sonraki boyamadan önce çalışmayı dener ve genellikle yavaş bir etkileşim oluşturur. Bunun aksine, requestAnimationFrame + setTimeout, requestPostAnimationFrame için basit bir çoklu dolgu oluşturur ve bir sonraki boyamadan sonra geri çağırmayı çalıştırır.

Kodun tamamını göster: raf+task.html

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

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

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

Ergonomik için bir söz verebilirsiniz:

Kodun tamamını göster: raf+task2.html

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

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

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

15. Birden çok etkileşim (ve yönlendirme tıklamaları)

Engelleme işlemini uzun süre yapmak işe yarayabilir 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ışma sürümünü tekrar deneyin (veya son adımda çalışmayı ertelemek için kendi varyasyonunuzu ortaya koyduysanız):

Kodun tamamını inceleyin: zaman aşımı_100.html

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

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

Hızlı bir şekilde birden çok kez tıklarsanız ne olur?

Performans izleme

Her tıklama için sırada bir saniyelik uzun bir görev bulunur. Bu sayede, ana iş parçacığı uzun süre boyunca engellenir.

Ana iş parçacığında birden çok saniye uzunluğunda görev nedeniyle etkileşimlerin 800 ms kadar yavaş olmasına neden oluyor

Bu uzun görevler, gelen yeni tıklamalarla çakıştığında, etkinlik işleyici neredeyse anında geri dönse bile yavaş etkileşimlere neden olur. Giriş gecikmeleriyle ilgili önceki denemede de aynı durumu oluşturduk. Ancak bu sefer giriş gecikmesi bir setInterval kaynağından değil, önceki etkinlik işleyiciler tarafından tetiklenen çalışmadan kaynaklanmaktadır.

Stratejiler

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

  • Gereksiz kodları, özellikle de komut dosyalarını tamamen kaldırın.
  • Uzun görevlerden kaçınmak için kodu optimize edin.
  • Yeni etkileşimler geldiğinde eski işi iptal edin.

16. 1. Strateji: Ardışık tekrar

Klasik bir strateji. Etkileşimler art arda hızlı bir şekilde arka arkaya geldiğinde ve işleme ya da ağ etkileri pahalı olduğunda, işlemi bilinçli olarak başlatmayı geciktirerek işlemi iptal edip yeniden başlatabilirsiniz. Bu kalıp, otomatik tamamlama alanları gibi kullanıcı arayüzleri için kullanışlıdır.

  • Pahalı işleri (ör. 500-1.000 milisaniye) bir zamanlayıcıyla başlatmayı ertelemek için setTimeout kullanın.
  • Bunu yaparken zamanlayıcı kimliğini kaydedin.
  • Yeni bir etkileşim gelirse clearTimeout düğmesini kullanarak önceki zamanlayıcıyı iptal edin.

Kodun tamamını inceleyin: debounce.html

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

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

Performans izleme

Birden çok etkileşim var ancak tüm bunların sonucunda tek bir uzun süreli iş yükü var.

Birden fazla tıklamaya rağmen yalnızca bir blockFor görevi çalışır ve çalıştırılmadan önce tam bir saniye boyunca tıklama almayana kadar bekler. Metin girişi yazmak veya birden çok hızlı tıklama alması beklenen öğe hedefleri gibi seri işlemler için bu, varsayılan olarak kullanılacak ideal bir stratejidir.

17. 2. Strateji: uzun süreli işleri kesintiye uğratın

Yine de geri sekmenin süresi dolduktan hemen sonra yeni bir tıklamanın gelmesi, bu uzun görevin ortasına gelmesi ve giriş gecikmesi nedeniyle çok yavaş bir etkileşime dönüşme olasılığı hâlâ düşüktür.

İdeal olarak, bir etkileşim işimizin ortasında gelirse yeni etkileşimlerin hemen ele alınması için yoğun çalışmalarımızı duraklatmak isteriz. Bunu nasıl yapabiliriz?

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

Çok sayıda setTimeout

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

Kodun tamamını inceleyin: 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 özellik, tarayıcının her bir görevi ayrı ayrı planlamasına olanak tanır ve giriş daha yüksek önceliğe sahip olabilir.

Birden fazla etkileşim ancak planlanmış tüm işler çok sayıda küçük göreve bölündü

Tekrar beş tıklamayla beş saniyelik bir çalışmaya geri dönmüş olduk. Ancak tıklama başına bir saniyelik görev, on adet 100 milisaniyelik göreve bölündü. Sonuç olarak, söz konusu görevlerle çakışan birden fazla etkileşim olsa bile hiçbir etkileşimin 100 milisaniyeden uzun bir giriş gecikmesi olmaz. Tarayıcı, setTimeout işinde gelen etkinlik işleyicilere öncelik verir ve etkileşimler yanıt vermeye devam eder.

Bu strateji, özellikle ayrı giriş noktaları planlandığında çok işe yarar. Örneğin, uygulamanın yükleme zamanında çağırmanız gereken bir dizi bağımsız özellik varsa işe yarar. Sadece komut dosyalarını yükleyip her şeyi komut dosyası değerlendirme zamanında çalıştırmak varsayılan olarak büyük ve uzun bir görevde her şeyi çalıştırabilir.

Ancak bu strateji, paylaşılan durumu kullanan bir for döngüsü gibi birbirlerine sıkı sıkıya bağlı kodları ayırmak için de işe yaramaz.

Artık yield() ile birlikte

Ancak "getiri noktalarını" kolayca eklemek için modern async ve await özelliklerinden yararlanabiliriz kullanabilirsiniz.

Örneğin:

Kodun tamamını inceleyin: getiriy.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 yığın çalışmadan sonra oluşturulur ve tarayıcı gelen tüm etkileşimlere yanıt verebilir. Ancak artık ayrı setTimeout yerine bir await schedulerDotYield() gereken tek şeydir. Bu sayede, for döngüsünün ortasında bile kullanılabilecek kadar ergonomiktir.

Artık AbortContoller() ile birlikte

Bu işe yaradı ancak her etkileşim, yeni etkileşimler gelip yapılması gereken işleri değiştirmiş olsa bile daha fazla işin programlanmasını sağlıyor.

Geri döndürme 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:

Kodun tamamını inceleyin: 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, yapılması gereken işlemi yaparak blockInPiecesYieldyAborty for döngüsünü başlatır ve düzenli olarak ana iş parçacığını vererek tarayıcının yeni etkileşimlere yanıt vermeye devam etmesini sağlar.

İ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. Bir dahaki sefere ilk döngünün tekrar çalıştırılması planlandığında signal.aborted değerinin artık true olduğunu fark eder ve başka bir işlem yapmadan hemen geri döner.

Ana iş parçacığı işi artık çok sayıda küçük parçaya dönüşüyor, etkileşimler kısa sürüyor ve iş sadece gerektiği kadar sürüyor

18. Sonuç

Tüm uzun görevleri ayırmak, sitenin yeni etkileşimlere yanıt vermesine olanak tanır. Bu, ilk geri bildirimleri hızlı bir şekilde sağlamanıza ve ayrıca devam etmekte olan işi iptal etmek gibi kararlar almanıza olanak tanır. Bazen bu, giriş noktalarını ayrı görevler olarak planlamak anlamına gelir. Bazen bu, "getiri" eklemek anlamına gelir noktaları belirleyeceğiz.

Unutmayın

  • INP tüm etkileşimleri ölçer.
  • Her etkileşim, girişten sonraki boyama kadar, yani kullanıcının duyarlılığı görme şekli ölçülür.
  • Giriş gecikmesi, etkinlik işleme süresi ve sunum gecikmesinin tümü etkileşimin yanıt verme süresini etkiler.
  • INP ve etkileşim dökümlerini Geliştirici Araçları ile kolayca ölçebilirsiniz.

Stratejiler

  • Sayfalarınızda uzun süreli kod (uzun görevler) olmamalıdır.
  • Bir sonraki boyama sonrasına kadar gereksiz kodu etkinlik işleyicilerin dışına taşıyın.
  • Oluşturma güncellemesinin tarayıcı için verimli olduğundan emin olun.

Daha fazla bilgi