isInputPending() ile daha iyi JS planlaması

Yük performansı ile giriş duyarlılığı arasındaki dengeyi önlemenize yardımcı olabilecek yeni bir JavaScript API'si.

Nate Schloss
Nate Schloss
Andrew Comminos
Andrew Comminos

Hızlı yüklenmesi zordur. İçeriklerini oluşturmak için JS'yi kullanan sitelerin şu anda yükleme performansı ile giriş yanıt hızı arasında bir denge kurması gerekmektedir: Görüntü için gereken tüm işleri bir kerede yapmak (daha iyi yükleme performansı, daha kötü giriş duyarlılığı) veya girişe ve boyamaya duyarlı olmaya devam etmek için işi daha küçük görevlere ayırma (daha kötü yükleme performansı, daha iyi giriş duyarlılığı).

Facebook, bu ödün vermeden yanıt verme ihtiyacını ortadan kaldırmak için Chromium'da isInputPending() API'yi önerdi ve uygulamaya koydu. Böylece, getiri sağlamadan yanıt verme süresini iyileştirebilirsiniz. Kaynak denemesiyle ilgili geri bildirim doğrultusunda, API'de birkaç güncelleme yaptık ve API'nin artık Chromium 87'de varsayılan olarak gönderildiğini duyurmaktan mutluluk duyuyoruz.

Tarayıcı uyumluluğu

Tarayıcı Desteği

  • 87
  • 87
  • x
  • x

isInputPending(), sürüm 87'den itibaren Chromium tabanlı tarayıcılarda gönderilir. Başka hiçbir tarayıcı API'yi göndermek istediğini belirtmedi.

Arka plan

Günümüzün JS ekosistemindeki işlerin çoğu tek bir iş parçacığında, yani ana iş parçacığında gerçekleştirilir. Bu, geliştiriciler için sağlam bir yürütme modeli sağlar ancak komut dosyası uzun süre çalışırsa kullanıcı deneyimi (özellikle yanıt verme durumu) önemli ölçüde etkilenebilir. Örneğin, bir giriş etkinliği tetiklenirken sayfa çok fazla iş yapıyorsa bu işlem tamamlanana kadar sayfa tıklama giriş etkinliğini işlemez.

Şu anda en iyi uygulama, bu sorunu JavaScript'i daha küçük bloklara ayırarak çözmektir. Sayfa yüklenirken sayfa bir miktar JavaScript çalıştırabilir ve kontrolü tekrar tarayıcıya aktarabilir. Tarayıcı daha sonra giriş etkinliği sırasını kontrol edebilir ve sayfaya bildirmesi gereken bir şey olup olmadığını görebilir. Daha sonra, tarayıcı JavaScript blokları eklendikçe tekrar çalıştırmaya devam edebilir. Bu işlem yardımcı olur ancak başka sorunlara da neden olabilir.

Sayfa, kontrolü tekrar tarayıcıya her verdiğinde, tarayıcının giriş etkinlik sırasını kontrol etmesi, etkinlikleri işlemesi ve bir sonraki JavaScript bloğunu alması biraz zaman alır. Tarayıcı etkinliklere daha hızlı yanıt verse de sayfanın toplam yükleme süresi yavaşlar. Çok sık alışveriş yaparsak sayfa çok yavaş yüklenir. Daha az sıklıkla yanıt verirsek tarayıcının kullanıcı etkinliklerine yanıt vermesi daha uzun sürer ve kullanıcılar hayal kırıklığına uğrar. Eğlenceli değildi.

Uzun JS görevlerini çalıştırdığınızda tarayıcının etkinlikleri dağıtmak için daha az zamanı olduğunu gösteren bir diyagram.

Facebook'ta, bu sinir bozucu dengeyi ortadan kaldıracak yeni bir yükleme yaklaşımı bulduğumuzda nasıl bir şey olacağını görmek istiyorduk. Bu konuda Chrome'daki arkadaşlarımıza ulaştık ve isInputPending() için teklifte bulunduk. isInputPending() API, web'deki kullanıcı girişleri için kesme kavramını kullanan ilk uygulamadır ve JavaScript'in tarayıcıya ulaşmadan girişleri kontrol edebilmesine olanak tanır.

isInputPending() ifadesini gösteren bir diyagram, JS'nizin tarayıcıda yürütülmesini tamamen sağlamadan bekleyen kullanıcı girişi olup olmadığını kontrol etmesini sağlar.

API'ye yönelik ilgi olduğundan, özelliği Chromium'da uygulamak ve göndermek için Chrome'daki meslektaşlarımızla iş ortaklığı yaptık. Chrome mühendislerinin yardımıyla, yamaların bir kaynak denemesi (Chrome'un bir API'yi tam olarak yayınlamadan önce değişiklikleri test edip geliştiricilerden geri bildirim almasının bir yoludur) arkasında kullanıma sunduk.

Şimdi kaynak denemesinden ve W3C Web Performansı Çalışma Grubu'nun diğer üyelerinden geri bildirim alıp API'de değişiklikler yaptık.

Örnek: getiri planlayıcı

Bileşenlerden işaretleme oluşturma, asalları çıkarma veya yalnızca iyi bir yükleme döner simgesi çizme gibi, sayfanızı yüklemek için yapmanız gereken bir dizi görüntüleme engelleme çalışması olduğunu varsayalım. Bunların her biri ayrı bir çalışma öğesine ayrılır. Planlayıcı kalıbını kullanarak, çalışmamızı varsayımsal bir processWorkQueue() işleviyle nasıl işleyeceğimizi ana hatlarıyla ortaya koyalım:

const DEADLINE = performance.now() + QUANTUM;
while (workQueue.length > 0) {
  if (performance.now() >= DEADLINE) {
    // Yield the event loop if we're out of time.
    setTimeout(processWorkQueue);
    return;
  }
  let job = workQueue.shift();
  job.execute();
}

processWorkQueue() daha sonra setTimeout() aracılığıyla yeni bir makro görevde çağrıldığında, tarayıcının girişlere duyarlı kalabilmesini (iş devam ettirilmeden önce etkinlik işleyicileri çalıştırabilmesini) ve nispeten kesintisiz olarak çalışabilmesini sağlarız. Yine de etkinlik döngüsünün kontrol edilmesini isteyen başka çalışmalar nedeniyle planlamamızı uzun bir süre alabiliriz veya etkinlik gecikmesinde QUANTUM milisaniyeye kadar ek bir süreye varan ekstra bir gecikme yaşanabilir.

Bu gayet normal ama sizin için daha iyi bir seçim yapabilir miyiz? Elbette!

const DEADLINE = performance.now() + QUANTUM;
while (workQueue.length > 0) {
  if (navigator.scheduling.isInputPending() || performance.now() >= DEADLINE) {
    // Yield if we have to handle an input event, or we're out of time.
    setTimeout(processWorkQueue);
    return;
  }
  let job = workQueue.shift();
  job.execute();
}

navigator.scheduling.isInputPending() için bir çağrı sunarak girişlere daha hızlı yanıt verebiliyor ve görüntüleme engelleme çalışmamızın kesintisiz olarak yürütülmesini sağlayabiliyoruz. İş tamamlanana kadar giriş dışında bir şeyle (ör. boyama) ilgilenmiyorsak QUANTUM uzunluğunu da kolaylıkla artırabiliriz.

Varsayılan olarak, "sürekli" etkinlikler isInputPending() öğesinden döndürülmez. mousemove, pointermove ve diğerleri buna dahildir. Bunların da karşılığını almak istiyorsanız hiç sorun değil. isInputPending() öğesine includeContinuous öğesinin true değerine ayarlandığı bir nesne sağlayarak devam edebiliriz:

const DEADLINE = performance.now() + QUANTUM;
const options = { includeContinuous: true };
while (workQueue.length > 0) {
  if (navigator.scheduling.isInputPending(options) || performance.now() >= DEADLINE) {
    // Yield if we have to handle an input event (any of them!), or we're out of time.
    setTimeout(processWorkQueue);
    return;
  }
  let job = workQueue.shift();
  job.execute();
}

İşte bu kadar. React gibi çerçeveler, benzer bir mantık kullanarak temel planlama kitaplıklarına isInputPending() desteği ekliyor. Bunun, bu çerçeveleri kullanan geliştiricilerin önemli yeniden yazma işlemleri olmadan perde arkasında isInputPending() avantajlarından faydalanmalarını sağlayacağını umuyoruz.

Yenilik yapmak her zaman kötü değildir

Daha az verim yapmanın her kullanım alanı için doğru çözüm olmadığını unutmayın. Kontrolü tarayıcıya döndürmenin, giriş etkinliklerini işlemek dışında birçok nedeni vardır. Örneğin, oluşturma işlemini gerçekleştirmek ve sayfadaki diğer komut dosyalarını yürütmek gibi işlemler bu kapsama girer.

Tarayıcının beklemedeki giriş etkinliklerini doğru şekilde ilişkilendiremediği durumlar vardır. Özellikle, kaynaklar arası iframe'ler için karmaşık klipler ve maskeler ayarlamak yanlış negatifler bildirebilir (yani isInputPending(), bu kareleri hedeflerken beklenmedik bir şekilde "false" döndürebilir). Siteniz stilize alt çerçevelerle etkileşim gerektiriyorsa yeterince sık getiri sağladığınızdan emin olun.

Bir etkinlik döngüsünü paylaşan diğer sayfalara da dikkat edin. Android için Chrome gibi platformlarda, birden çok kaynağın bir etkinlik döngüsünü paylaşması oldukça yaygın bir durumdur. Kaynaklar arası çerçeveye giriş gönderilirse isInputPending(), hiçbir zaman true değerini döndürmez. Bu nedenle, arka plana alınan sayfalar ön plan sayfalarının duyarlılığını etkileyebilir. Sayfa Görünürlük API'sini kullanarak arka planda çalışırken sıklığı azaltmak, ertelemek veya daha fazla getiri elde etmek isteyebilirsiniz.

isInputPending() hizmetini dikkatli bir şekilde kullanmanızı öneririz. Kullanıcıları engelleyen bir işlem yoksa daha sık getiri sağlayarak etkinlik döngüsündeki diğer kullanıcılara karşı nazik olun. Uzun görevler zararlı olabilir.

Geri bildirim

  • is-input-pending deposundaki spesifikasyonla ilgili geri bildirim bırakın.
  • Twitter'da @acomminos (özellik yazarlarından biri) ile iletişime geçin.

Sonuç

isInputPending() ürününün kullanıma sunulmasından ve geliştiricilerin bu hizmeti hemen kullanmaya başlayabilmesinden heyecan duyuyoruz. Bu API, Facebook'un ilk kez yeni bir web API'si geliştirdi ve bunu fikir geliştirme aşamasından standart teklif teklifine, yani tarayıcıda kargoya taşıdı. Bu noktaya gelmemize yardımcı olan herkese teşekkür eder, bu fikri ortaya koymamıza ve göndermemize yardımcı olan Chrome'daki herkese özel bir teşekkürlerimizi sunarız!

Will H McMahan'ın Unsplash'teki hero fotoğrafı.