isInputPending() ile daha iyi JS planlaması

Yükleme performansı ile giriş duyarlılığı arasındaki dengeyi sağlamanıza yardımcı olabilecek yeni bir JavaScript API'si.

Nate Schloss
Nate Schloss
Andrew Comminos
Andrew Comminos

Hızlı yüklemek zor. İçeriklerini oluşturmak için JS'den yararlanan sitelerin şu anda yükleme performansı ile giriş duyarlılığı arasında bir denge kurması gerekiyor: Görüntüleme için gereken tüm işlemleri tek seferde gerçekleştirebilir (daha iyi yükleme performansı, daha kötü giriş duyarlılığı) veya girişe ve boyamaya duyarlı kalabilmek için işi daha küçük görevlere bölebilir (daha kötü yükleme performansı, daha iyi giriş duyarlılığı).

Bu dengenin sağlanmasına gerek kalmaması için Facebook, performanstan ödün vermeden yanıt vermeyi iyileştirmek amacıyla Chromium'da isInputPending() API'yi önerdi ve uyguladı. Kaynak denemesinden gelen geri bildirimler doğrultusunda API'de bir dizi güncelleme yaptık. API'nin artık Chromium 87'de varsayılan olarak kullanıma sunulduğunu duyurmaktan mutluluk duyuyoruz.

Tarayıcı uyumluluğu

Tarayıcı desteği

  • Chrome: 87.
  • Edge: 87.
  • Firefox: Desteklenmez.
  • Safari: Desteklenmez.

Kaynak

isInputPending(), Chromium tabanlı tarayıcıların 87 sürümünden itibaren gönderilir. Başka hiçbir tarayıcı API'yi kullanıma sunma niyetini belirtmedi.

Arka plan

Günümüzün JS ekosistemindeki çalışmaların çoğu tek bir mesaj dizisinde (ana mesaj dizisi) yapılır. Bu, geliştiricilere güçlü bir yürütme modeli sağlar ancak komut dosyası uzun süre boyunca yürütülürse kullanıcı deneyimi (özellikle de yanıt verme hızı) büyük ölçüde etkilenebilir. Örneğin, bir giriş etkinliği tetiklenirken sayfa çok fazla işlem yapıyorsa sayfa, bu işlem tamamlanana kadar tıklama giriş etkinliğini işlemez.

Mevcut en iyi uygulama, JavaScript'i daha küçük bloklara ayırarak bu sorunu çözmektir. Sayfa yüklenirken biraz JavaScript çalıştırabilir ve ardından kontrolü tarayıcıya geri verebilir. Tarayıcı daha sonra giriş etkinliği sırasını kontrol edip sayfaya bildirmesi gereken bir şey olup olmadığını görebilir. Ardından tarayıcı, JavaScript blokları eklendikçe bunları çalıştırmaya geri dönebilir. Bu, sorunun çözülmesine yardımcı olsa da başka sorunlara yol açabilir.

Sayfa kontrolü tarayıcıya her geri verdiğinde, tarayıcının giriş etkinliği kuyruğunu kontrol etmesi, etkinlikleri işlemesi ve sonraki JavaScript bloğunu alması biraz zaman alır. Tarayıcı etkinliklere daha hızlı yanıt verse de sayfanın genel yükleme süresi yavaşlar. Çok sık teslim olursak sayfa çok yavaş yüklenir. Daha seyrek izin verirsek tarayıcının kullanıcı etkinliklerine yanıt vermesi daha uzun sürer ve kullanıcılar can sıkıcı bir durumla karşılaşır. Eğlenceli değil.

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

Facebook'da, bu can sıkıcı dengeyi ortadan kaldıracak yeni bir yükleme yaklaşımı geliştirdiğimizde neler olacağını görmek istedik. Bu konuda Chrome'daki arkadaşlarımızla iletişime geçtik ve isInputPending() önerisini geliştirdik. isInputPending() API, web'de kullanıcı girişleri için kesinti kavramını kullanan ilk API'dir ve JavaScript'in tarayıcıya vermeden girişleri kontrol etmesine olanak tanır.

isInputPending() işlevini gösteren bir şema, JS'nizin yürütmeyi tarayıcıya tamamen geri döndürmeden bekleyen kullanıcı girişi olup olmadığını kontrol etmesini sağlar.

API'ye yönelik ilgi olduğundan bu özelliği Chromium'da uygulayıp göndermek için Chrome'daki meslektaşlarımızla birlikte çalıştık. Chrome mühendislerinin yardımıyla, 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) sonrasında yamaları kullanıma sunduk.

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

Örnek: getiri planlayıcısı

Sayfanızı yüklemek için yapmanız gereken, bileşenlerden işaretleme oluşturma, primerleri çıkarma veya sadece düzgün yüklenen bir döner simge çizme gibi bir dizi görüntüleme engelleme çalışmanızın olduğunu varsayalım. Bunların her biri ayrı bir iş öğesine ayrılır. Planlayıcı kalıbını kullanarak, varsayımsal bir processWorkQueue() işlevinde işimizi nasıl işleyebileceğimizi özetleyelim:

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();
}

Daha sonra setTimeout() üzerinden yeni bir makro görevinde processWorkQueue() çağrısı yaparak, tarayıcının girişlere biraz duyarlı kalmasını (iş devam etmeden önce etkinlik işleyicileri çalıştırabilir) ve nispeten kesintisiz olarak çalışmasını sağladık. Ancak, etkinlik döngüsünün kontrolünü isteyen diğer işler tarafından uzun süre boyunca planımızdan çıkarılabiliriz veya QUANTUM milisaniye ek etkinlik gecikmesi yaşayabiliriz.

Bu iyi, ancak daha iyisini yapabilir miyiz? Kesinlikle!

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() çağrısı ekleyerek, girişe daha hızlı yanıt verirken ekranı engelleme çalışmamızın kesintisiz şekilde yürütülmesini sağlayabiliriz. İş tamamlanana kadar girdi (ör. boyama) dışında bir şeyle ilgilenmiyorsak QUANTUM uzunluğunu da kolayca artırabiliriz.

Varsayılan olarak, "sürekli" etkinlikler isInputPending() kaynağından döndürülmez. Bunlar mousemove, pointermove ve diğerleri arasındadır. Bu içerikler için de gelir elde etmek istiyorsanız sorun değil. includeContinuous özelliğini true olarak ayarlayarak isInputPending() için bir nesne sağladığımızda hazırız demektir:

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 oluşturuyor. Bu sayede, bu çerçeveleri kullanan geliştiricilerin önemli düzeyde yeniden yazımlara gerek kalmadan isInputPending() kamera arkasından faydalanabilmelerini umuyoruz.

Geri çekilmek her zaman kötü değildir

Daha az verim elde etmenin her kullanım alanı için doğru çözüm olmadığını belirtmek isteriz. Giriş etkinliklerini işleme dışında, kontrolü tarayıcıya döndürmenin birçok nedeni vardır. Örneğin, sayfayı oluşturmak ve sayfadaki diğer komut dosyalarını yürütmek için de kontrolü tarayıcıya döndürebilirsiniz.

Tarayıcının, bekleyen giriş etkinliklerini doğru şekilde ilişkilendiremediği durumlar vardır. Özellikle, kaynak ötesi iFrame'ler için karmaşık klipler ve maskeler ayarlamak yanlış negatif raporlayabilir (yani isInputPending() bu çerçeveleri hedeflerken beklenmedik bir şekilde yanlış döndürebilir). Sitenizde stilize alt çerçevelerle etkileşim gerekiyorsa yeterince sık verim verdiğinizden emin olun.

Etkinlik döngüsü paylaşan diğer sayfalara da dikkat edin. Android için Chrome gibi platformlarda, birden fazla kaynağın aynı etkinlik döngüsünü paylaşması oldukça yaygın bir durumdur. Giriş, kaynak ötesi bir çerçeveye dağıtılırsa isInputPending() hiçbir zaman true değerini döndürmez. Bu nedenle, arka plandaki sayfalar ön plandaki sayfaların duyarlılığını etkileyebilir. Page Görünürlük API'sini kullanarak arka planda çalışma yaparken getiriyi azaltmak, ertelemek veya daha sık kullanmak isteyebilirsiniz.

isInputPending() değerini dikkatli bir şekilde kullanmanızı öneririz. Kullanıcıyı engelleme işlemi yapmanız gerekmiyorsa daha sık sonuç vererek etkinlik döngüsündeki diğer kullanıcılara yardımcı olun. Uzun görevler zararlı olabilir.

Geri bildirim

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

Sonuç

isInputPending()'ün kullanıma sunulması ve geliştiricilerin bu özelliği hemen kullanmaya başlayabilmesi bizi heyecanlandırıyor. Bu API, Facebook'ın ilk kez yeni bir web API'si oluşturup bu API'yi fikir aşamasından standart önerisine ve ardından bir tarayıcıda kullanıma sunma aşamasına taşımasıdır. Bu noktaya gelmemize yardımcı olan herkese teşekkür etmek ve bu fikri hayata geçirmemize ve kullanıma sunmamıza yardımcı olan Chrome ekibindeki herkese özel olarak teşekkür etmek isteriz.

Unsplash'taki Will H McMahan tarafından çekilen hero fotoğrafı.