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 First Giriş Gecikmesi (FID) yerine Core Web Vitals olarak kullanılmaya hazır.
Web geliştiricilerin web sitelerini olabildiğince hızlı hale getirmelerine yardımcı olacak yeni API'ler sunma çabamızın bir parçası olarak Chrome Ekibi, Chrome'un 115 sürümünden itibaren scheduler.yield
için kaynak denemesini çalıştırmaya devam ediyor. 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 yürütmek için tamamlamaya 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. Her şey olacaktır, her şeyin ne zaman olacağı önemli değildir ve bir an önce gerçekleşecektir. 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 vermesini geciktirdiği için sayfanın yanıt verme hızının düşük olmasına yol açar. 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.
Bununla birlikte, kodunuzun tarayıcıda bir görev başlatması, kontrol ana iş parçacığına geri verilmeden önce 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 işe yarar, çünkü setTimeout
işlevine iletilen geri çağırma, kalan çalışmayı daha sonra yürütme için sıraya alınacak ayrı bir göreve taşıyacaktır. 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: Verimli noktanın sonrasında gelen iş, 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 bunların 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üğü,
setInterval
ile engelleme görevi çalıştırıldı ifadesini içeren çeşitli mesajlarla doldurulur. - Daha sonra, Döngüyü çalıştır (her yinelemede
setTimeout
sonucunu verir) 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östermektedir: 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'un 115 sürümünden beri deneysel web platformu özelliği olarak kullanılmaktadır. "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 geri çağırma işlevinin gelecekte daha ileri bir zamanda çalışacak şekilde planlanmasının güzel bir yan etkisi olduğunu belirtmek gerekir. Ancak setTimeout
ile verirken kalan işin görev kuyruğunun arkasına gönderildiğini unutmayın. Varsayılan olarak scheduler.yield
, kalan işi kuyruğun başına gönderir. Diğer bir deyişle, çıktıktan hemen sonra devam ettirmek istediğiniz iş, diğer kaynaklardaki görevlerde arka planda yer almayacaktır (kullanıcı etkileşimleri hariç).
scheduler.yield
, ana iş parçacığına dönüşen ve çağrıldığında 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.
- Üstteki Görevleri düzenli olarak çalıştır etiketli düğmeyi tıklayın.
- Son olarak, Döngüyü çalıştır (her yinelemede
scheduler.yield
sonucunu verir) 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 verim sağlayan demodakinin aksine, döngünün her iterasyondan sonra verim sağlamasına rağmen kalan işi kuyruğun arkasına değil, önüne gönderdiğini görebilirsiniz. Bu, her iki yöntemin de en iyisini sağlar: Web sitenizde giriş duyarlılığını artırabilir, ancak aynı zamanda bitirmek istediğiniz çalışmanın getiri sonra verilmediğinden de emin olabilirsiniz.
Mutlaka deneyin!
scheduler.yield
ilginizi çekiyorsa ve denemek istiyorsanız, Chrome'un 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:
- Görevleri planlamak için uygulamanızda zaten
scheduler.postTask
kullanıyorsunuz. - Görev ve verim öncelikleri belirleyebilmek istiyorsunuz.
scheduler.postTask
API'nin sunduğuTaskController
sınıfı aracılığıyla görevleri iptal edebilmek veya yeniden önceliklendirebilmek istiyorsunuz.
Bu, durumunuzu açıklamıyorsa çoklu dolgu size 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 işe yarayabilir, ancak tahmin edebileceğiniz gibi, scheduler.yield
özelliğini desteklemeyen tarayıcılar "sıranın önü" davranışı olmadan veri verir. Bu, hiç getiri elde etmek istemediğiniz anlamına geliyorsa kullanılabiliyorsa scheduler.yield
kullanan ancak yoksa hiç getiri 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
API'nin kullanışlı olduğunu düşünüyorsanız API'nin iyileştirilmesine yardımcı olmak için lütfen araştırmamıza katılın ve nasıl daha da geliştirilebileceği konusunda geri bildirimde bulunun.
Jonathan Allison tarafından Unsplash'tan alınan lokomotif resim.