Kullanıcı girişlerine hızlı yanıt veren web siteleri oluşturmak, web performansının en zorlu yönlerinden biri olmuştur. Bu, Chrome Ekibi'nin web geliştiricilerinin uyum sağlamasına yardımcı olmak için sıkı bir şekilde çalıştığı bir çalışmadır. Yalnızca bu yıl, Sonraki Boyamayla Etkileşim (INP) metriğinin deneyselden beklemede durumuna geçeceği duyurulmuştu. 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
, zamanlayıcı API'sine eklenen ve geleneksel olarak kullanılan yöntemlere kıyasla ana iş parçacığına kontrol sağlamanın hem daha kolay hem de daha iyi bir yolunu sunan yeni bir öneridir.
Getiri
JavaScript, görevleri yürütmek için tamamlamayı çalıştırma modelini kullanır. Bu durum, bir görevin ana iş parçacığında çalışması durumunda söz konusu görevin tamamlanması için gereken süre boyunca çalışacağı anlamına gelir. Bir görevin tamamlanmasının ardından kontrol, ana iş parçacığına geri getirilir. Bu, ana iş parçacığının sıradaki bir sonraki görevi işlemesine olanak tanır.
Bir görevin hiç bitmediği aşırı durumların (ör. sonsuz bir döngü) yanı sıra getiri, JavaScript görev planlama mantığının kaçınılmaz bir yönüdür. Her şey olacaktır, her şeyin ne zaman olacağı önemli değildir ve bir an önce gerçekleşecektir. Görevlerin çalıştırılması ç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 sayfaların yanıt verme performansını olumsuz etkiler. Görevler ne kadar sık gerçekleşir ve ne kadar uzun sürerse kullanıcıların sayfanın yavaşladığı, hatta tamamen bozulmuş olduğu izlenimi edinme olasılığı da 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 veri vererek sayfadaki kullanıcı girişine duyarlılığı artırabilirsiniz. Bu, bir sonraki uygun fırsatta görevi bitecek ş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 kazanabilir.
![Görevleri ayırmanın girişlere daha iyi yanıt vermeyi nasıl kolaylaştırabileceğinin tasviri. Üst kısımda, uzun bir görev, görev tamamlanana kadar bir etkinlik işleyicinin çalışmasını engelliyor. En altta, parçalanmış görev, etkinlik işleyicinin normalde olacağından daha erken çalışmasına olanak tanır.](https://developer.chrome.com/static/blog/introducing-scheduler-yield-origin-trial/image/a-depiction-how-breaking-1f8a83887e582.png?authuser=5&hl=tr)
Açık bir şekilde verim verdiğinizde, tarayıcıya "Hey, yapmak üzere olduğum işin biraz zaman alabileceğini anlıyorum ve kullanıcı girişine veya önemli olabilecek diğer görevlere yanıt vermeden önce bu işin tümünü yapmak zorunda kalmanızı istemiyorum" demiş olursunuz. Geliştiricinin araç kutusunda yer alan ve kullanıcı deneyimini iyileştirmeye yönelik oldukça faydalı bir araçtır.
Mevcut getiri stratejileriyle ilgili sorun
Yaygın bir getiri yöntemi, 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 çalışmasını beklemek yerine, "bu büyük işi parçaya küçük parçalara ayıralım" diyorsunuz.
Bununla birlikte, setTimeout
ile veri sağlamanın istenmeyen bir yan etkisi olabilir: Getiri noktasından sonra gelen çalışma, görev sırasının arkasına gider. Kullanıcı etkileşimlerine göre planlanan görevler de olması gerektiği gibi sırada en başta yer almaya devam eder. Ancak açık bir şekilde yapıldıktan sonra yapmak istediğiniz geri kalan işler, öncesinde sıraya alınmış rakip kaynaklardan gelen diğer görevler nedeniyle daha da gecikebilir.
Bu özelliğin nasıl uygulandığını görmek için bu Glitch demosunu deneyin veya aşağıdaki yerleşik sürümde deneyin. Demo, tıklayabileceğiniz birkaç düğme ve bunların altında görevler yürütüldüğünde günlüğe kaydedilen bir kutudan oluşur. Sayfaya geldiğinizde aşağıdaki işlemleri gerçekleştirin:
- Üstteki Görevleri düzenli olarak çalıştır etiketli düğmeyi tıklayarak, engelleme görevlerini düzenli aralıklarla çalışacak şekilde planlayabilirsiniz. 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.
Demonun alt kısmındaki kutuda şuna benzer bir metin göreceksiniz:
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 çıkışta "görev sırasının sonu" gösterilmektedir setTimeout
ile getiri elde edilirken ortaya çıkan davranıştır. Beş öğeyi çalıştıran döngüde beş öğe işlenir ve her biri işlendikten sonra setTimeout
değerine sahip olur.
Bu, web'de sık karşılaşılan bir sorunu göstermektedir: Bir komut dosyasının (özellikle de üçüncü taraf bir komut dosyası), belirli bir aralıkta çalışma çalıştıran bir kronometre işlevi kaydetmesi için olağan dışı bir durum değildir. "Görev sırasının sonu" setTimeout
ile birlikte gelen bir davranış ise diğer görev kaynaklarından gelen çalışmaların, döngünün teslim ettikten sonra yapması gereken geri kalan işlerden önce sıraya alınabileceği anlamına gelir.
Uygulamanıza bağlı olarak bu istenen bir sonuç olabilir veya olmayabilir. Ancak çoğu durumda, geliştiricilerin ana iş parçacığının kontrolünü bu kadar kolay bir şekilde bırakmak istememelerinin nedeni budur. Kullanıcı etkileşimleri daha erken gerçekleşebileceği için getiri iyidir ancak kullanıcı dışı diğer etkileşimlerle de ana iş parçacığında zaman kazanılmasına olanak tanır. Bu gerçekten ciddi bir sorun ama scheduler.yield
çözümüne 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. Aklınıza takılan sorulardan biri şudur: "setTimeout
zaten şunu yaparken getirilmesi için özel bir işleve neden ihtiyacım var?"
Getirinin setTimeout
için bir tasarım hedefi değil, 0
zaman aşımı değeri belirtilmiş olsa bile bir geri çağırma ileride daha sonraki bir zamanda çalışacak şekilde planlamanın güzel bir yan etkisi olduğunu belirtmek isteriz. Ancak unutulmaması gereken daha önemli nokta, setTimeout
ile sonuç vermenin kalan çalışmaları görev sırasının geri gönderdiğidir. Varsayılan olarak scheduler.yield
, kalan çalışmayı sıranın önüne gönderir. Diğer bir deyişle, çıktıktan hemen sonra devam ettirmek istediğiniz işin diğer kaynaklardaki görevlerde arka planda kalmayacağı (kullanıcı etkileşimlerinin önemli istisnaları dışında) anlamına gelir.
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
yapabilirsiniz:
async function yieldy () {
// Do some work...
// ...
// Yield!
await scheduler.yield();
// Do some more work...
// ...
}
scheduler.yield
adlı uygulamanın nasıl çalıştığını görmek için aşağıdakileri yapın:
chrome://flags
adresine gidiş rotasını izle.- 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 yer alan çıkış şu şekilde görünür:
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
kullanılarak elde edilen demonun aksine, döngünün (her iterasyondan sonra verilmeye rağmen) kalan çalışmayı sıranın 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
ile yerel olarak deneme yapmak istiyorsanız Chrome'un adres çubuğunachrome://flags
yazıp buraya yazın ve Deneysel Web Platformu Özellikleri bölümündeki açılır listeden Etkinleştir'i seçin. Bu işlem,scheduler.yield
ürününün (ve diğer tüm deneysel özelliklerin) yalnızca sizin Chrome örneğinizde kullanılabilmesini sağlar.- Herkesin erişebileceği bir kaynaktaki gerçek Chromium kullanıcıları için
scheduler.yield
hizmetini etkinleştirmek istersenizscheduler.yield
kaynak denemesine kaydolmanız gerekir. Bu sayede, önerilen özelliklerle belirli bir süre için güvenli bir şekilde denemeler yapabilir ve Chrome Ekibi'ne, bu özelliklerin alanda nasıl kullanıldığına dair değerli bilgiler sağlayabilirsiniz. Kaynak denemelerinin işleyiş şekli hakkında daha fazla bilgi için bu rehberi okuyun.
scheduler.yield
ürününü kullanmayan tarayıcıları desteklemeye devam ederken bu hizmeti nasıl kullanacağınız, hedeflerinize bağlıdır. Resmi çoklu dolguyu kullanabilirsiniz. Aşağıdakiler sizin için geçerliyse çoklu dolgu yararlıdır:
- Görevleri planlamak için uygulamanızda zaten
scheduler.postTask
kullanıyorsunuz. - Görevleri ve getiri önceliklerini 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 göre olmayabilir. Bu durumda, kendi yedeğinizi birkaç şekilde alabilirsiniz. İlk yaklaşım, mevcutsa scheduler.yield
kullanır ancak yoksa setTimeout
değerine 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ü" olmadan veri verecektir gösterir. 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
, Scheduler API için heyecan verici bir katkıdır. Bu API'nin, geliştiricilerin mevcut getiri stratejilerine kıyasla yanıt vermeyi iyileştirmelerini kolaylaştıracağını umuyoruz. 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'ın Unsplash'teki hero resim.