Chrome 88 (Ocak 2021), belirli durumlarda gizli sayfalar için zincirleme JavaScript zamanlayıcılarını büyük ölçüde kısıtlayacaktır. Bu işlem CPU kullanımını azaltarak pil kullanımını da azaltır. Bunun davranışı değiştirdiği bazı uç durumlar vardır ancak zamanlayıcılar genellikle farklı bir API'nin daha verimli ve güvenilir olacağı yerlerde kullanılır.
Bu jargon oldukça ağır ve biraz belirsizdi. Konuya yakından bakalım...
Terminoloji
Gizli sayfalar
Genel olarak gizli, farklı bir sekmenin etkin olduğu veya pencerenin küçültüldüğü anlamına gelir. Ancak tarayıcılar, içeriği tamamen görünmezken bir sayfanın gizlendiğini kabul edebilir. Burada bazı tarayıcılar diğerlerinden daha gelişmiştir ancak tarayıcının görünürlüğün değiştiğini düşündüğünde bunu izlemek için her zaman page visibility API'yi kullanabilirsiniz.
JavaScript zamanlayıcılar
JavaScript zamanlayıcıları ile setTimeout
ve setInterval
özelliklerini kastediyorum. Bu özellikler, ileride bir geri arama
planlamanıza olanak tanır. Zamanlayıcılar faydalıdır ve kullanımdan kalkmaz, ancak bazen bir etkinliğin daha verimli ve daha doğru olacağı durumlarda yoklamak için kullanılırlar.
Zincirleme zamanlayıcılar
setTimeout
geri çağırmasıyla aynı görevde setTimeout
yöntemini çağırırsanız ikinci çağrı "zincirli" olur. setInterval
ile her yineleme, zincirinin bir parçasıdır. Bunu daha kolay anlamak için kod kullanabilirsiniz:
let chainCount = 0;
setInterval(() => {
chainCount++;
console.log(`This is number ${chainCount} in the chain`);
}, 500);
Ve:
let chainCount = 0;
function setTimeoutChain() {
setTimeout(() => {
chainCount++;
console.log(`This is number ${chainCount} in the chain`);
setTimeoutChain();
}, 500);
}
Kısıtlama nasıl çalışır?
Kısıtlama işlemi aşamalı olarak gerçekleşir:
Minimum sınırlama
Bu durum, aşağıdakilerden herhangi biri geçerli olduğunda programlanmış zamanlayıcılarda görülür:
- Sayfa görünür olmalıdır.
- Sayfa son 30 saniye içinde gürültü yapmaya başladı. Bu, ses oluşturma API'lerinin herhangi birinden olabilir ancak sessiz ses parçası sayılmaz.
İstenen zaman aşımı süresi 4 ms'den az olmadığı ve zincir sayısı 5 ya da daha fazla olmadığı sürece (zaman aşımı 4 ms'ye ayarlanacak) zamanlayıcı kısıtlanmaz. Bu yeni bir şey değil. Tarayıcılar bunu yıllardır yapıyor.
Kısıtlama
Bu durum, minimum kısıtlama uygulanmadığında programlanmış zamanlayıcılarda geçerli olur ve aşağıdakilerden herhangi biri doğrudur:
- zincir sayısı 5'ten azsa.
- Sayfa 5 dakikadan daha kısa süredir gizlendi.
- WebRTC kullanılıyor. Özellikle, "açık"
RTCDataChannel
veya "canlı"MediaStreamTrack
içeren birRTCPeerConnection
öğesi vardır.
Tarayıcı, bu gruptaki zamanlayıcıları saniyede bir kez kontrol eder. Bunlar saniyede yalnızca bir kez kontrol edildiğinden, benzer bir zaman aşımına sahip kronometreler birlikte gruplandırılarak sekmenin kodu çalıştırması için gereken süre birleştirilir. Bu da yeni bir şey değil; tarayıcılar bunu yıllardır bir dereceye kadar yapıyor.
Yoğun kısıtlama
Tamam, işte Chrome 88'deki yeni bölüm. Planlanmış zamanlayıcılarda minimum kısıtlama veya kısıtlama koşullarından hiçbiri geçerli olmadığında ve aşağıdaki koşulların tümü geçerli olduğunda yoğun kısıtlama uygulanır:
- Sayfa 5 dakikadan uzun süredir gizlenmiş.
- zincir sayısı 5 veya daha fazlaysa.
- Sayfa en az 30 saniyedir sessiz kalmıştır.
- WebRTC kullanılmıyor.
Bu durumda tarayıcı, bu gruptaki zamanlayıcıları dakika başına bir kez kontrol eder. Daha önce olduğu gibi bu, zamanlayıcıların dakika dakika kontroller halinde gruplanacağı anlamına gelir.
Geçici çözümler
Genellikle zamanlayıcı yerine daha iyi bir alternatif vardır veya zamanlayıcılar CPU'ya ve pil ömrüne daha duyarlı olması için başka bir şeyle birleştirilebilir.
Eyalet oylaması
Bu, zamanlayıcıların en yaygın (yanlış) kullanımıdır. Zamanlayıcılar bir şeylerin değişip değişmediğini görmek için sürekli olarak kontrol etmek veya anket yapmak için kullanılırlar. Çoğu durumda, değişiklik gerçekleştiğinde bunların size bildirdiği bir push vardır. Böylece sürekli kontrol etmek zorunda kalmazsınız. Aynı sonucu elde eden bir olay olup olmadığına bakın.
Bazı örnekler:
- Bir öğenin görüntü alanına ne zaman girdiğini öğrenmek istiyorsanız
IntersectionObserver
değerini kullanın. - Bir öğenin boyutu değiştiğinde bunu öğrenmek istiyorsanız
ResizeObserver
değerini kullanın. - DOM'un ne zaman değiştiğini bilmeniz gerekiyorsa
MutationObserver
veya özel öğe yaşam döngüsü geri çağırmalarını kullanın. - Bir sunucuyu yoklamak yerine, web yuvalarını, sunucu tarafından gönderilen etkinlikleri, push mesajlarını veya akışları getirmeyi tercih edin.
- Ses/videoda sahne değişikliklerine tepki vermeniz gerekiyorsa
timeupdate
veended
gibi etkinlikleri kullanın. Her karede bir işlem yapmanız gerekiyorsarequestVideoFrameCallback
gibi etkinlikleri kullanın.
Bildirimi belirli bir zamanda göstermek isterseniz bildirim tetikleyicileri de vardır.
Animasyonlar
Animasyon görsel öğelerdir. Bu nedenle, sayfa gizlendiğinde CPU zamanını kullanmamalıdır.
requestAnimationFrame
, animasyon çalışmalarını programlama konusunda JavaScript zamanlayıcılarından çok daha iyidir. Cihazın yenileme hızıyla senkronize edilir ve görüntülenebilir her kare için yalnızca bir geri çağırma olmasını sağlar ve bu kareyi oluşturmak için gereken maksimum süreyi elde edersiniz. Ayrıca requestAnimationFrame
, sayfanın görünmesini bekler ve böylece sayfa gizlendiğinde CPU kullanmaz.
Animasyonunuzun tamamını önceden beyan edebiliyorsanız CSS animasyonlarını veya web animasyonları API'sini kullanmayı düşünün. Bunların
requestAnimationFrame
ile aynı avantajları vardır ancak tarayıcı, otomatik birleştirme gibi ek optimizasyonlar gerçekleştirebilir ve
genellikle bunların kullanımı daha kolaydır.
Animasyonunuzun kare hızı düşükse (yanıp sönen imleç gibi) zamanlayıcılar şu anda en iyi seçenektir ancak her ikisinden de en iyi şekilde yararlanmak için zamanlayıcıları requestAnimationFrame
ile birleştirebilirsiniz:
function animationInterval(ms, signal, callback) {
const start = document.timeline.currentTime;
function frame(time) {
if (signal.aborted) return;
callback(time);
scheduleFrame(time);
}
function scheduleFrame(time) {
const elapsed = time - start;
const roundedElapsed = Math.round(elapsed / ms) * ms;
const targetNext = start + roundedElapsed + ms;
const delay = targetNext - performance.now();
setTimeout(() => requestAnimationFrame(frame), delay);
}
scheduleFrame(start);
}
Kullanım:
const controller = new AbortController();
// Create an animation callback every second:
animationInterval(1000, controller.signal, time => {
console.log('tick!', time);
});
// And stop it:
controller.abort();
Test
Bu değişiklik, Chrome 88 sürümündeki (Ocak 2021) tüm Chrome kullanıcıları için etkinleştirilecektir. Bu özellik şu anda Chrome Beta, Yeni geliştirilenler ve Canary kullanıcılarının% 50'si için etkindir. Test etmek istiyorsanız Chrome Beta, Dev veya Canary'yi başlatırken şu komut satırı işaretini kullanın:
--enable-features="IntensiveWakeUpThrottling:grace_period_seconds/10,OptOutZeroTimeoutTimersFromThrottling,AllowAggressiveThrottlingWithWebSocket"
grace_period_seconds/10
bağımsız değişkeni, 5 dakikanın tamamı yerine gizlendikten 10 saniye sonra sayfa gizlendikten sonra yoğun kısıtlamaya neden olur. Böylece kısıtlamanın etkisini görebilirsiniz.
Gelecek
Zamanlayıcılar aşırı CPU kullanımının bir kaynağı olduğundan, web içeriğini bozmadan onları kısıtlama yollarına ve kullanım alanlarına uygun hale getirmek için ekleyip değiştirebileceğimiz API'lere bakmaya devam edeceğiz. Kişisel olarak, düşük frekanslı etkili animasyon geri çağırmaları yerine animationInterval
ihtiyacını ortadan kaldırmak istiyorum. Sorularınız varsa lütfen Twitter'dan bize ulaşın!
Unsplash'ta Heather Zabriskie tarafından yüklenen başlık fotoğrafı.