Kullanıcı girişlerine hızlı yanıt veren web siteleri oluşturmak, web performansının en zorlu yönlerinden biridir. Chrome Ekibi, web geliştiricilerine bu konuda yardımcı olmak için yoğun şekilde çalışmaktadır. Bu yıl, Interaction to Next Paint (INP) metriğinin deneysel durumdan bekleme durumuna geçeceği duyuruldu. Mart 2024'te Core Web Vital olarak First Input Delay (FID)'in yerini alacak.
Web geliştiricilerin web sitelerini olabildiğince hızlı hale getirmelerine yardımcı olacak yeni API'ler sunma çabalarını sürdüren Chrome Ekibi, şu anda Chrome'un 115 sürümünden itibaren scheduler.yield
için kaynak deneme sürümü yayınlıyor. scheduler.yield
, planlayıcı API'sine önerilen yeni bir eklentidir. Bu eklenti, geleneksel olarak kullanılan yöntemlerden daha kolay ve daha iyi bir şekilde ana iş parçacığına kontrol vermeyi sağlar.
Verirken
JavaScript, görevleri işlemek için sonuna kadar çalıştırma modelini kullanır. Bu, bir görev ana iş parçacığında çalışırken tamamlanması için gereken süre boyunca çalıştığı anlamına gelir. Bir görevin tamamlanmasının ardından kontrol, ana iş parçacığına iade edilir. Bu sayede ana iş parçacığı, sıradaki görevi işleyebilir.
Bir görevin hiçbir zaman bitmediği aşırı durumlar (ör. sonsuz döngü) dışında, JavaScript'in görev planlama mantığının kaçınılmaz bir yönü, görevden vazgeçmektir. Bu durum gerçekleşecektir. Tek sorun ne zaman gerçekleşeceğidir. Ne kadar erken olursa o kadar iyidir. Görevlerin çalışması çok uzun sürdüğünde (tam olarak 50 milisaniyeden uzun) uzun görevler olarak kabul edilir.
Uzun görevler, tarayıcının kullanıcı girişine yanıt verme özelliğini geciktirdiği için sayfanın tepkisinin düşük olmasına neden olur. Uzun görevler ne kadar sık gerçekleşirse ve ne kadar uzun sürerse kullanıcıların sayfanın yavaş olduğu veya tamamen bozuk olduğu izlenimini alma olasılığı o kadar artar.
Ancak, kodunuzun tarayıcıda bir görevi başlatması, kontrolün ana iş parçacığına geri verilmesi için bu görevin tamamlanmasını beklemeniz gerektiği anlamına gelmez. Bir görevde açıkça yield yaparak sayfadaki kullanıcı girişine verilen yanıtı iyileştirebilirsiniz. Bu işlem, görevi bir sonraki uygun fırsatta tamamlanacak şekilde böler. Bu sayede diğer görevler, uzun görevlerin tamamlanmasını beklemek zorunda kalmadan ana iş parçacığında daha erken zaman alabilir.
Açıkça yield yaptığınızda tarayıcıya "Yapacağım işin biraz zaman alabileceğinin farkındayım ve kullanıcı girişine veya önemli olabilecek diğer görevlere yanıt vermeden önce bu işin tümünü yapmanız gerekmesini istemiyorum" demiş olursunuz. Geliştiricilerin araç kutusundaki bu değerli araç, kullanıcı deneyimini iyileştirme konusunda çok faydalı olabilir.
Mevcut getiri stratejileriyle ilgili sorun
Sonuç döndürmeyle ilgili yaygın bir yöntem, 0
zaman aşımı değeriyle setTimeout
kullanır. Bu, setTimeout
işlevine iletilen geri çağırma işlevi, kalan işi sonraki yürütme için sıraya eklenecek ayrı bir göreve taşıdığı için işe yarar. Tarayıcının kendi kendine teslim olmasını beklemek yerine, "Bu büyük iş parçasını daha küçük parçalara ayıralım" diyorsunuz.
Ancak setTimeout
ile verim elde etmenin istenmeyen bir yan etkisi olabilir: Verim noktasından sonra gelen işler görev kuyruğunun sonuna gider. Kullanıcı etkileşimleriyle planlanan görevler, olması gerektiği gibi kuyruğun önüne geçer. Ancak açıkça verdikten sonra yapmak istediğiniz kalan iş, kendisinden önce kuyruğa eklenen rakip kaynaklardan gelen diğer görevler tarafından daha da gecikebilir.
Bu özelliğin nasıl çalıştığını görmek için bu Glitch demosunu deneyin veya aşağıdaki yerleşik sürümde denemeler yapın. Demo, tıklayabileceğiniz birkaç düğmeden ve bu düğmelerin altında görevlerin ne zaman çalıştırıldığını kaydeden bir kutudan oluşur. Sayfaya geldiğinizde aşağıdaki işlemleri yapın:
- Görevleri düzenli olarak çalıştır etiketli en üstteki düğmeyi tıklayın. Bu düğme, engelleyen görevleri belirli aralıklarla çalıştıracak şekilde planlar. Bu düğmeyi tıkladığınızda görev günlüğüne
setInterval
ile engelleme görevi çalıştırıldı yazan birkaç mesaj eklenir. - Ardından, Döngüyü çalıştır, her iterasyonda
setTimeout
ile sonuç ver etiketli düğmeyi tıklayın.
Demo'nun alt kısmındaki kutuda şuna benzer bir ifade görürsünüz:
Processing loop item 1
Processing loop item 2
Ran blocking task via setInterval
Processing loop item 3
Ran blocking task via setInterval
Processing loop item 4
Ran blocking task via setInterval
Processing loop item 5
Ran blocking task via setInterval
Ran blocking task via setInterval
Bu çıktı, setTimeout
ile yield kullanıldığında ortaya çıkan "görev kuyruğunun sonu" davranışını gösterir. Çalışan döngü beş öğeyi işler ve her biri işlendikten sonra setTimeout
döndürür.
Bu, web'de yaygın bir sorunu gösterir: Bir komut dosyasının (özellikle üçüncü taraf komut dosyalarının) belirli aralıklarla çalışan bir zamanlayıcı işlevi kaydetmesi normaldir. setTimeout
ile verim sağlamanın getirdiği "görev kuyruğunun sonu" davranışı, diğer görev kaynaklarından gelen işlerin, döngünün verim sağladıktan sonra yapması gereken kalan işin önüne geçebileceği anlamına gelir.
Bu, uygulamanıza bağlı olarak istenilen bir sonuç olabilir veya olmayabilir. Ancak çoğu durumda geliştiriciler, ana iş parçacığının kontrolünü bu kadar kolay bırakmak istemez. Kullanıcı etkileşimlerinin daha erken çalışma fırsatı bulduğu için verim verme iyidir ancak kullanıcı etkileşimi olmayan diğer çalışmaların da ana iş parçacığında zaman almasına olanak tanır. Bu gerçek bir sorundur ancak scheduler.yield
bu sorunu çözmenize yardımcı olabilir.
scheduler.yield
girişinden girin
scheduler.yield
, Chrome 115 sürümünden beri bir işaretin arkasında deneysel web platformu özelliği olarak kullanılabilir. "setTimeout
zaten verim verirken neden özel bir işleve ihtiyacım var?" diye düşünebilirsiniz.
Yielding'in setTimeout
'ün tasarım hedefi olmadığını, 0
zaman aşımı değeri belirtilmiş olsa bile gelecekte daha sonraki bir noktada çalışacak şekilde bir geri çağırma planlamanın güzel bir yan etkisi olduğunu belirtmek gerekir. Ancak daha da önemlisi, setTimeout
ile teslim vermenin kalan işi görev kuyruğunun arkasına göndermesidir. Varsayılan olarak scheduler.yield
, kalan işi kuyruğun başına gönderir. Bu, devretme işleminden hemen sonra devam etmek istediğiniz işin, diğer kaynaklardan gelen görevlere (kullanıcı etkileşimleri hariç) göre ikinci plana atılmayacağı anlamına gelir.
scheduler.yield
, ana mesaj dizisine yol veren ve çağrıldığında bir Promise
döndüren bir işlevdir. Yani bir async
işlevinde await
kullanabilirsiniz:
async function yieldy () {
// Do some work...
// ...
// Yield!
await scheduler.yield();
// Do some more work...
// ...
}
scheduler.yield
'ü çalışırken görmek için aşağıdakileri yapın:
chrome://flags
adresine gidin.- Deneysel Web Platformu özellikleri denemesini etkinleştirin. Bunu yaptıktan sonra Chrome'u yeniden başlatmanız gerekebilir.
- Demo sayfasına gidin veya bu listenin altındaki yerleşik sürümünü kullanın.
- Görevleri düzenli olarak çalıştır etiketli en üstteki düğmeyi tıklayın.
- Son olarak, Döngüyü çalıştır, her iterasyonda
scheduler.yield
ile sonuç ver etiketli düğmeyi tıklayın.
Sayfanın alt kısmındaki kutuda şuna benzer bir çıktı gösterilir:
Processing loop item 1
Processing loop item 2
Processing loop item 3
Processing loop item 4
Processing loop item 5
Ran blocking task via setInterval
Ran blocking task via setInterval
Ran blocking task via setInterval
Ran blocking task via setInterval
Ran blocking task via setInterval
setTimeout
kullanarak yield yapan demodakinin aksine, döngünün her iterasyondan sonra yield vermesine rağmen kalan işi kuyruğun arkasına değil, önüne gönderdiğini görebilirsiniz. Bu sayede iki dünyanın da en iyisini elde edebilirsiniz: Web sitenizdeki giriş duyarlılığını artırmak için teslim olabilir, ancak teslim sonrasında tamamlamak istediğiniz işin gecikmesini önleyebilirsiniz.
Mutlaka deneyin!
scheduler.yield
ilginizi çekiyorsa ve denemek istiyorsanız Chrome 115 sürümünden itibaren bunu iki şekilde yapabilirsiniz:
scheduler.yield
özelliğini yerel olarak denemek istiyorsanız Chrome'un adres çubuğunachrome://flags
yazın ve Deneysel Web Platformu Özellikleri bölümündeki açılır menüden Etkinleştir'i seçin. Bu durumdascheduler.yield
(ve diğer deneysel özellikler) yalnızca Chrome örneğinizde kullanılabilir.- Herkese açık bir kaynakta gerçek Chromium kullanıcıları için
scheduler.yield
'ü etkinleştirmek istiyorsanızscheduler.yield
kaynak denemesine kaydolmanız gerekir. Bu sayede, önerilen özellikleri belirli bir süre boyunca güvenli bir şekilde deneyebilir ve Chrome Ekibi'ne bu özelliklerin sahada nasıl kullanıldığına dair değerli bilgiler sağlayabilirsiniz. Kaynak denemelerinin işleyiş şekli hakkında daha fazla bilgi için bu kılavuzu okuyun.
scheduler.yield
'ü kullanma şekliniz (bu özelliği uygulamayan tarayıcıları desteklemeye devam ederken), hedeflerinize bağlıdır. Resmi polyfill'i kullanabilirsiniz. Aşağıdakiler durumunuz için geçerliyse polyfill faydalıdır:
- Uygulamanızda görevleri planlamak için zaten
scheduler.postTask
kullanıyorsanız. - Görev ve verim öncelikleri belirleyebilmek istiyorsunuz.
scheduler.postTask
API'sinin sunduğuTaskController
sınıfı aracılığıyla görevleri iptal etmek veya yeniden önceliklendirmek istiyorsunuz.
Bu durum sizin durumunuz değilse polyfill sizin için uygun olmayabilir. Bu durumda, kendi yedek planınızı birkaç şekilde kullanabilirsiniz. İlk yaklaşım, varsa scheduler.yield
değerini kullanır, yoksa setTimeout
değerine geri döner:
// A function for shimming scheduler.yield and setTimeout:
function yieldToMain () {
// Use scheduler.yield if it exists:
if ('scheduler' in window && 'yield' in scheduler) {
return scheduler.yield();
}
// Fall back to setTimeout:
return new Promise(resolve => {
setTimeout(resolve, 0);
});
}
// Example usage:
async function doWork () {
// Do some work:
// ...
await yieldToMain();
// Do some other work:
// ...
}
Bu yöntem işe yarayabilir ancak tahmin edebileceğiniz gibi, scheduler.yield
özelliğini desteklemeyen tarayıcılar "sıranın başında" davranışı olmadan sonuç verir. Bu, hiç verim elde etmek istemediğiniz anlamına geliyorsa mevcutsa scheduler.yield
kullanan ancak mevcut değilse hiç verim sağlamayan başka bir yaklaşımı deneyebilirsiniz:
// A function for shimming scheduler.yield with no fallback:
function yieldToMain () {
// Use scheduler.yield if it exists:
if ('scheduler' in window && 'yield' in scheduler) {
return scheduler.yield();
}
// Fall back to nothing:
return;
}
// Example usage:
async function doWork () {
// Do some work:
// ...
await yieldToMain();
// Do some other work:
// ...
}
scheduler.yield
, planlayıcı API'sine eklenen heyecan verici bir özelliktir. Bu özellik, geliştiricilerin mevcut verim stratejilerine kıyasla yanıt vermeyi iyileştirmesini kolaylaştıracaktır. scheduler.yield
sizin için yararlı bir API gibi görünüyorsa lütfen API'yi iyileştirmemize yardımcı olmak için araştırmamıza katılın ve nasıl daha da iyileştirilebileceğiyle ilgili geri bildirimde bulunun.
Jonathan Allison tarafından Unsplash'tan alınan lokomotif resim.