Yayınlanma tarihi: 6 Mart 2025
Uzun görevler ana iş parçacığını meşgul ettiğinde sayfa yavaş ve yanıtsız görünür. Bu da kullanıcı girişine yanıt vermek gibi diğer önemli işleri yapmasını engeller. Sonuç olarak, yerleşik form kontrolleri bile kullanıcılara bozuk görünebilir (sayfa donmuş gibi). Daha karmaşık özel bileşenler ise bu durumdan daha da fazla etkilenir.
scheduler.yield()
, ana iş parçacığına teslim olmanın bir yoludur. Bu sayede tarayıcı, bekleyen yüksek öncelikli işleri çalıştırabilir ve ardından yürütmeyi bıraktığı yerden devam edebilir. Bu, sayfanın daha duyarlı olmasını sağlar ve Sonraki Boyamayla Etkileşim (INP) metriğini iyileştirmeye yardımcı olur.
scheduler.yield
, tam olarak söylediği şeyi yapan ergonomik bir API sunar: Çağrıldığı işlevin yürütülmesi await scheduler.yield()
ifadesinde duraklar ve görevi bölerek ana iş parçacığına verir. İş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 özel avantajı, verimden sonraki devam görevinin, sayfa tarafından sıraya alınmış diğer benzer görevlerin öncesinde çalıştırılması için planlanmasıdır. Yeni görev başlatmak yerine bir görevin devam etmesine öncelik verir.
setTimeout
veya scheduler.postTask
gibi işlevler de görevleri bölmek için kullanılabilir. Ancak bu devam işlevleri genellikle önceden sıraya alınmış yeni görevlerin sonrasında çalıştırılır. Bu da ana iş parçacığına verme ve işlerini tamamlama arasında uzun gecikmeler yaşanmasına neden olabilir.
Verdikten sonra öncelikli devam ettirmeler
scheduler.yield
, Öncelikli Görev Planlama API'sinin bir parçasıdır. Web geliştiricileri olarak, etkinlik döngüsünün görevleri açık öncelikler açısından çalıştırma sırası hakkında genellikle konuşmayız. Ancak göreceli öncelikler her zaman vardır. Örneğin, sıraya eklenen setTimeout
geri çağırmalarının ardından çalışan bir requestIdleCallback
geri çağırma veya genellikle setTimeout(callback, 0)
ile sıraya eklenen bir görevden önce çalışan bir tetiklenen giriş etkinliği dinleyicisi.
Öncelikli Görev Planlama, bu durumu daha açık hale getirerek hangi görevin diğerinden önce çalışacağını belirlemeyi kolaylaştırır ve gerekirse yürütme sırasını değiştirmek için öncelikleri ayarlamanızı sağlar.
Daha önce de belirtildiği gibi, scheduler.yield()
ile yield verdikten sonra bir işlevin devam eden yürütmesi, diğer görevleri başlatmaktan daha yüksek bir önceliğe sahiptir. Temel kavram, diğer görevlere geçmeden önce bir görevin devamının önce çalıştırılmasıdır. Görev, tarayıcının diğer önemli işleri (ör. kullanıcı girişine yanıt vermek) yapabilmesi için düzenli olarak yield yapan iyi huylu bir kodsa diğer benzer görevlerden sonra öncelik verilmesi nedeniyle yield yaptığı için cezalandırılmamalıdır.
Aşağıda bir örnek verilmiştir: setTimeout
kullanılarak farklı görevlerde çalışacak şekilde sıraya eklenen iki işlev.
setTimeout(myJob);
setTimeout(someoneElsesJob);
Bu örnekte, iki setTimeout
çağrısı birbirinin hemen yanındadır ancak gerçek bir sayfada, çalıştırılacak işi bağımsız olarak ayarlayan bir birinci taraf komut dosyası ve üçüncü taraf komut dosyası gibi tamamen farklı yerlerde çağrılabilir veya çerçevenizin planlayıcısının derinliklerinde tetiklenen ayrı bileşenlerden iki görev olabilir.
Bu çalışma, Geliştirici Araçları'nda aşağıdaki gibi görünebilir:
myJob
uzun görev olarak işaretlenir ve tarayıcı çalışırken başka bir şey 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 çalışacak şekilde planlandı ancak bu planlama, someoneElsesJob
zaten planlandıktan sonra çalıştırılıyor. Bu nedenle, yürütme aşağıdaki gibi olur:
Tarayıcı, myJob
işleminin ortasında duyarlı olabilmesi için görevi setTimeout
ile böldük ancak şimdi myJob
işleminin ikinci kısmı yalnızca someoneElsesJob
tamamlandıktan sonra çalışıyor.
Bazı durumlarda bu sorunsuz olabilir ancak genellikle en iyi seçenek değildir. myJob
, ana iş parçacığına tamamen vazgeçmek için değil, sayfanın kullanıcı girişlerine duyarlı kalmasını sağlamak için ana iş parçacığına yol veriyordu. someoneElsesJob
'ün özellikle yavaş olduğu veya someoneElsesJob
dışında başka birçok işin de planlandığı durumlarda, someoneElsesJob
'ün ikinci yarısının çalıştırılması uzun zaman alabilir.myJob
Geliştiricinin setTimeout
öğesini myJob
'e ekleme amacı muhtemelen bu değildi.
scheduler.yield()
değerini girin. Bu değer, kendisini çağıran herhangi bir işlevin devamını, diğer benzer görevleri başlatmaktan biraz daha yüksek öncelikli bir kuyruğa yerleştirir. myJob
, bunu 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ı, duyarlı olma fırsatına sahip olmaya devam eder ancak artık myJob
görevinin devam ettirilmesine, yeni someoneElsesJob
görevinin başlatılmasına göre öncelik verilir. Bu nedenle myJob
, someoneElsesJob
başlamadan önce tamamlanır. Bu, ana iş parçacığından tamamen vazgeçmek yerine, yanıt vermeyi sürdürmek için ana iş parçacığına verme beklentisine çok daha yakındır.
Öncelikli devralma
Daha kapsamlı Öncelikli Görev Planlama API'sinin bir parçası olarak scheduler.yield()
, scheduler.postTask()
'te bulunan açık önceliklerle iyi bir uyum sağlar. Öncelik açıkça ayarlanmazsa scheduler.postTask()
geri çağırma işlevi içindeki scheduler.yield()
temel olarak önceki örnekle aynı şekilde çalışır.
Ancak bir öncelik belirlenirse (ör. düşük 'background'
önceliği kullanılırsa):
async function lowPriorityJob() {
part1();
await scheduler.yield();
part2();
}
scheduler.postTask(lowPriorityJob, {priority: 'background'});
Devam görevi, diğer 'background'
görevlerinden daha yüksek bir öncelikle planlanır. Bu sayede, bekleyen 'background'
işlerinden önce öncelikli olarak devam görevi alınır. Ancak bu görev, diğer varsayılan veya yüksek öncelikli görevlerden daha düşük önceliğe sahip olmaya devam eder ve 'background'
işi olarak kalır.
Bu, düşük öncelikli bir işi 'background'
scheduler.postTask()
(veya requestIdleCallback
) ile planlarsanız, içindeki scheduler.yield()
'ten sonraki devam işleminin de diğer görevlerin çoğu tamamlanana ve ana iş parçacığı çalışmaya hazır olana kadar beklediği anlamına gelir. Bu da düşük öncelikli bir işte yield'den tam olarak istediğiniz şeydir.
API nasıl kullanılır?
scheduler.yield()
şu anda yalnızca Chromium tabanlı tarayıcılarda kullanılabilir. Bu nedenle, bu özelliği kullanmak için özelliği algılamanız ve diğer tarayıcılar için ikincil bir verme yöntemine geri dönmeniz gerekir.
scheduler-polyfill
, scheduler.postTask
ve scheduler.yield
için küçük bir polyfill'dir. Diğer tarayıcılarda planlama API'lerinin gücünün büyük bir kısmını taklit etmek için dahili olarak bir yöntem kombinasyonu kullanır (scheduler.yield()
öncelik devralma özelliği desteklenmez).
polyfill kullanmaktan kaçınmak isteyenler için bir yöntem, setTimeout()
kullanarak yield vermek ve öncelikli devamın kaybedilmesini kabul etmektir. Hatta bu kabul edilemezse desteklenmeyen tarayıcılarda yield vermemektir. Daha fazla bilgi için "Uzun görevleri optimize etme" bölümündeki scheduler.yield()
belgelerini inceleyin.
scheduler.yield()
özelliğini algılayıp yedek eklerseniz wicg-task-scheduling
türleri, tür kontrolü ve IDE desteği almak için de kullanılabilir.
Daha fazla bilgi
API ve bu API'nin görev öncelikleri ve scheduler.postTask()
ile nasıl etkileşime geçtiği hakkında daha fazla bilgi için MDN'deki scheduler.yield()
ve Öncelikli Görev Planlama dokümanlarına göz atın.
Uzun görevler, kullanıcı deneyimini nasıl etkilediği ve bu görevlerle ilgili olarak neler yapılabileceği hakkında daha fazla bilgi edinmek için uzun görevleri optimize etme başlıklı makaleyi okuyun.