Yayınlanma tarihi: 6 Mart 2025
Uzun görevler ana iş parçacığını meşgul ederek kullanıcı girişine yanıt verme gibi diğer önemli işleri yapmasını engellediğinde sayfa yavaş ve yanıt vermiyormuş gibi görünür. Sonuç olarak, daha karmaşık özel bileşenleri bir kenara bırakın, yerleşik form kontrolleri bile kullanıcılara bozuk görünebilir (sayfa donmuş gibi).
scheduler.yield()
, ana iş parçacığına geçiş yapmanın bir yoludur. Tarayıcının bekleyen yüksek öncelikli işleri çalıştırmasına izin verir ve ardından yürütmeye kaldığı yerden devam eder. Bu, sayfanın daha duyarlı olmasını sağlar ve dolayısıyla Sonraki Boyamayla Etkileşim (INP) değerinin iyileştirilmesine yardımcı olur.
scheduler.yield
, tam olarak söylediği şeyi yapan ergonomik bir API sunar: İçinde çağrıldığı işlevin yürütülmesi await scheduler.yield()
ifadesinde duraklatılır ve ana iş parçacığına bırakılarak görev bölünür. İşlevin geri kalanının yürütülmesi (işlevin devamı olarak adlandırılır) yeni bir etkinlik döngüsü görevinde çalışacak şekilde planlanır.
async function respondToUserClick() {
giveImmediateFeedback();
await scheduler.yield(); // Yield to the main thread.
slowerComputation();
}
scheduler.yield
'nın asıl avantajı, yield'dan sonraki devamın, sayfa tarafından sıraya alınmış diğer benzer görevler çalıştırılmadan önce çalıştırılacak şekilde planlanmasıdır. Yeni görevlere başlamak yerine mevcut görevlerin devamına öncelik verir.
setTimeout
veya scheduler.postTask
gibi işlevler de görevleri bölmek için kullanılabilir ancak bu devam ettirmeler genellikle halihazırda sıraya alınmış yeni görevlerden sonra çalışır. Bu da ana iş parçacığına geçiş ile işlerinin tamamlanması arasında uzun gecikmeler yaşanma riskini artırır.
Kontrolü bıraktıktan sonra öncelikli devam ettirme
scheduler.yield
, Prioritized Task Scheduling API'nin bir parçasıdır. Web geliştiriciler olarak, etkinlik döngüsünün görevleri hangi sırayla çalıştırdığı hakkında genellikle açık öncelikler açısından konuşmayız. Ancak göreceli öncelikler her zaman vardır. Örneğin, requestIdleCallback
kuyruğa alınmış geri çağırma işlemlerinden sonra çalışan bir geri çağırma işlemi setTimeout
veya tetiklenen bir giriş etkinliği dinleyicisi genellikle setTimeout(callback, 0)
ile kuyruğa alınmış bir görevden önce çalışır.
Öncelikli görev planlama, hangi görevin diğerinden önce çalışacağını anlamayı kolaylaştırarak bu durumu daha açık hale getirir ve gerekirse öncelikleri ayarlayarak yürütme sırasını değiştirmenize olanak tanır.
Belirtildiği gibi, scheduler.yield()
ile kontrolü bıraktıktan sonra bir işlevin yürütülmeye devam etmesi, diğer görevleri başlatmaktan daha yüksek önceliğe sahiptir. Buradaki temel kavram, diğer görevlere geçmeden önce bir görevin devamlılığının sağlanması gerektiğidir. Görev, tarayıcının diğer önemli işlemleri (ör. kullanıcı girişine yanıt verme) yapabilmesi için düzenli olarak kontrolü bırakan iyi davranışlı bir kodsa diğer benzer görevlerden sonra önceliklendirilerek kontrolü bıraktığı için cezalandırılmamalıdır.
İki işlevin setTimeout
kullanılarak farklı görevlerde çalıştırılmak üzere sıraya alındığı bir örneği aşağıda bulabilirsiniz.
setTimeout(myJob);
setTimeout(someoneElsesJob);
Bu örnekte, iki setTimeout
çağrısı yan yana olsa da gerçek bir sayfada tamamen farklı yerlerde çağrılabilir. Örneğin, birinci taraf komut dosyası ve üçüncü taraf komut dosyası, çalışacak işi bağımsız olarak ayarlayabilir veya ayrı bileşenlerden gelen iki görev, çerçevenizin zamanlayıcısında derinlemesine tetiklenebilir.
Bu çalışmanın DevTools'da nasıl görünebileceği aşağıda gösterilmiştir:
myJob
uzun bir görev olarak işaretlenir ve çalışırken tarayıcının başka bir işlem yapmasını engeller. Birinci taraf komut dosyasından geldiğini varsayarsak bunu şu şekilde ayırabiliriz:
function myJob() {
// Run part 1.
myJobPart1();
// Yield with setTimeout() to break up long task, then run part2.
setTimeout(myJobPart2, 0);
}
myJobPart2
, myJob
içinde setTimeout
ile birlikte çalışacak şekilde planlandığı ancak bu planlama someoneElsesJob
zaten planlandıktan sonra yapıldığı için yürütme şu şekilde görünür:
setTimeout
ile görevi böldük. Böylece tarayıcı, myJob
'nin ortasında yanıt verebiliyordu. Ancak artık myJob
'nin ikinci kısmı yalnızca someoneElsesJob
tamamlandıktan sonra çalışıyor.
Bu durum bazı durumlarda sorun olmayabilir ancak genellikle ideal değildir. myJob
, ana iş parçacığını tamamen bırakmak için değil, sayfanın kullanıcı girişine yanıt vermeye devam edebilmesi için ana iş parçacığına geçiyordu. someoneElsesJob
özellikle yavaş olduğunda veya someoneElsesJob
dışında başka birçok iş de planlandığında myJob
'nin ikinci yarısının çalıştırılması uzun zaman alabilir. Geliştirici, setTimeout
öğesini myJob
öğesine eklerken muhtemelen bu sonucu amaçlamamıştır.
scheduler.yield()
girin. Bu, onu çağıran işlevlerin devamını diğer benzer görevleri başlatmaktan biraz daha yüksek öncelikli bir sıraya yerleştirir. myJob
, bu özelliği kullanacak şekilde değiştirilirse:
async function myJob() {
// Run part 1.
myJobPart1();
// Yield with scheduler.yield() to break up long task, then run part2.
await scheduler.yield();
myJobPart2();
}
Şimdi yürütme şu şekilde görünür:
Tarayıcı yine yanıt verebilir ancak artık myJob
görevinin devamı, yeni görev someoneElsesJob
'nin başlatılmasından öncelikli olduğundan myJob
, someoneElsesJob
başlamadan önce tamamlanır. Bu, ana iş parçacığını tamamen bırakmak yerine yanıt verebilirliği korumak için ana iş parçacığına geçme beklentisine çok daha yakındır.
Öncelik devralma
Daha büyük olan Öncelikli Görev Zamanlama API'sinin bir parçası olarak scheduler.yield()
, scheduler.postTask()
'de bulunan açık önceliklerle iyi çalışır. Öncelik açıkça ayarlanmadığında, scheduler.postTask()
geri çağırması içindeki bir scheduler.yield()
temelde önceki örnekle aynı şekilde davranır.
Ancak düşük 'background'
önceliği kullanmak gibi bir öncelik ayarlanırsa:
async function lowPriorityJob() {
part1();
await scheduler.yield();
part2();
}
scheduler.postTask(lowPriorityJob, {priority: 'background'});
Devam etme işlemi, diğer 'background'
görevlerinden daha yüksek öncelik verilerek planlanır. Böylece, bekleyen 'background'
işlerinden önce beklenen öncelikli devam etme işlemi gerçekleştirilir. Ancak bu işlem, diğer varsayılan veya yüksek öncelikli görevlerden daha düşük önceliğe sahiptir ve 'background'
işi olarak kalır.
Bu nedenle, 'background'
scheduler.postTask()
ile (veya requestIdleCallback
ile) düşük öncelikli bir iş planlarsanız scheduler.yield()
içinde devam etme işlemi de diğer görevlerin çoğu tamamlanana ve ana ileti dizisi boşta kalana kadar bekler. Bu, düşük öncelikli bir işte bekleme işleminden istediğiniz şeydir.
API nasıl kullanılır?
scheduler.yield()
şu anda yalnızca Chromium tabanlı tarayıcılarda kullanılabilir. Bu nedenle, özelliği algılamanız ve diğer tarayıcılarda ikincil bir yöntemle geri dönmeniz gerekir.
scheduler-polyfill
, scheduler.postTask
ve scheduler.yield
için küçük bir polyfill'dir. Diğer tarayıcılardaki planlama API'lerinin gücünün çoğunu taklit etmek için dahili olarak bir yöntem kombinasyonu kullanır (ancak scheduler.yield()
öncelik devralma desteklenmez).
Polyfill kullanmak istemeyenler için bir yöntem, setTimeout()
kullanarak sonuç üretmek ve öncelikli devamlılık kaybını kabul etmektir. Bu kabul edilemezse desteklenmeyen tarayıcılarda sonuç üretilmez. Daha fazla bilgi için scheduler.yield()
Uzun görevleri optimize etme bölümündeki dokümanları inceleyin.
wicg-task-scheduling
türleri, scheduler.yield()
özelliğini algılıyorsanız ve geri dönüşü kendiniz ekliyorsanız tür kontrolü ve IDE desteği almak için de kullanılabilir.
Daha fazla bilgi
API ve görev öncelikleriyle scheduler.postTask()
ile etkileşimi hakkında daha fazla bilgi için MDN'deki scheduler.yield()
ve Öncelikli Görev Planlama belgelerine göz atın.
Uzun görevler, bunların kullanıcı deneyimini nasıl etkilediği ve bu görevlerle ilgili olarak yapılması gerekenler hakkında daha fazla bilgi edinmek için uzun görevleri optimize etme başlıklı makaleyi inceleyin.