Dipublikasikan: 6 Maret 2025
Halaman terasa lambat dan tidak responsif saat tugas yang lama membuat thread utama sibuk, sehingga mencegahnya melakukan pekerjaan penting lainnya, seperti merespons input pengguna. Akibatnya, bahkan kontrol formulir bawaan dapat terlihat rusak bagi pengguna—seolah-olah halaman dibekukan—apalagi komponen kustom yang lebih kompleks.
scheduler.yield()
adalah cara untuk menyerahkan thread utama—memungkinkan browser menjalankan tugas prioritas tinggi yang tertunda—lalu melanjutkan eksekusi dari tempat terakhir. Hal ini membuat halaman lebih responsif dan, pada akhirnya, membantu meningkatkan Interaction to Next Paint (INP).
scheduler.yield
menawarkan API ergonomis yang melakukan persis seperti yang dikatakan: eksekusi fungsi yang dipanggil dijeda pada ekspresi await scheduler.yield()
dan menghasilkan thread utama, yang membagi tugas. Eksekusi fungsi lainnya—disebut kelanjutan fungsi—akan dijadwalkan untuk berjalan dalam tugas loop peristiwa baru.
async function respondToUserClick() {
giveImmediateFeedback();
await scheduler.yield(); // Yield to the main thread.
slowerComputation();
}
Manfaat spesifik scheduler.yield
adalah bahwa kelanjutan setelah hasil dijadwalkan untuk dijalankan sebelum menjalankan tugas serupa lainnya yang telah diantrekan oleh halaman. Prioritasnya adalah melanjutkan tugas daripada memulai tugas baru.
Fungsi seperti setTimeout
atau scheduler.postTask
juga dapat digunakan untuk membagi tugas, tetapi kelanjutan tersebut biasanya berjalan setelah tugas baru yang sudah diantrekan, yang berpotensi menimbulkan penundaan yang lama antara menyerahkan ke thread utama dan menyelesaikan tugasnya.
Lanjutan yang diprioritaskan setelah menghasilkan
scheduler.yield
adalah bagian dari Prioritized Task Scheduling API. Sebagai developer web, kita biasanya tidak membahas urutan event loop menjalankan tugas dalam hal prioritas eksplisit, tetapi prioritas relatif selalu ada, seperti callback requestIdleCallback
yang berjalan setelah callback setTimeout
yang diantrekan, atau pemroses peristiwa input yang dipicu biasanya berjalan sebelum tugas diantrekan dengan setTimeout(callback, 0)
.
Penjadwalan Tugas yang Diprioritaskan hanya membuat hal ini lebih eksplisit, sehingga mempermudah untuk mengetahui tugas apa yang akan berjalan sebelum tugas lainnya, dan memungkinkan penyesuaian prioritas untuk mengubah urutan eksekusi tersebut, jika diperlukan.
Seperti yang disebutkan, eksekusi fungsi yang berlanjut setelah menghasilkan dengan scheduler.yield()
mendapatkan prioritas yang lebih tinggi daripada memulai tugas lain. Konsep panduannya adalah bahwa kelanjutan tugas harus dijalankan terlebih dahulu, sebelum melanjutkan ke tugas lain. Jika tugas adalah kode yang berperilaku baik yang menghasilkan secara berkala sehingga browser dapat melakukan hal penting lainnya (seperti merespons input pengguna), tugas tersebut tidak boleh dihukum karena menghasilkan dengan diprioritaskan setelah tugas serupa lainnya.
Berikut adalah contohnya: dua fungsi, yang diantrekan untuk dijalankan dalam tugas yang berbeda menggunakan setTimeout
.
setTimeout(myJob);
setTimeout(someoneElsesJob);
Dalam hal ini, kedua panggilan setTimeout
berada tepat di samping satu sama lain, tetapi di halaman sebenarnya, keduanya dapat dipanggil di tempat yang sama sekali berbeda, seperti skrip pihak pertama dan skrip pihak ketiga yang menyiapkan tugas secara independen untuk dijalankan, atau bisa jadi dua tugas dari komponen terpisah yang dipicu jauh di dalam penjadwal framework Anda.
Berikut adalah tampilan pekerjaan tersebut di DevTools:
myJob
ditandai sebagai tugas yang lama, sehingga memblokir browser untuk melakukan hal lain saat sedang berjalan. Dengan asumsi bahwa ini berasal dari skrip pihak pertama, kita dapat membaginya:
function myJob() {
// Run part 1.
myJobPart1();
// Yield with setTimeout() to break up long task, then run part2.
setTimeout(myJobPart2, 0);
}
Karena myJobPart2
dijadwalkan untuk berjalan dengan setTimeout
dalam myJob
, tetapi penjadwalan tersebut berjalan setelah someoneElsesJob
telah dijadwalkan, berikut tampilan eksekusi:
Kita telah membagi tugas dengan setTimeout
sehingga browser dapat responsif selama pertengahan myJob
, tetapi sekarang bagian kedua myJob
hanya berjalan setelah someoneElsesJob
selesai.
Dalam beberapa kasus, hal ini mungkin tidak masalah, tetapi biasanya tidak optimal. myJob
memberikan hasil ke thread utama untuk memastikan halaman tetap responsif terhadap input pengguna, bukan untuk melepaskan thread utama sepenuhnya. Jika someoneElsesJob
sangat lambat, atau banyak tugas lain selain someoneElsesJob
juga telah dijadwalkan, mungkin perlu waktu lama sebelum paruh kedua myJob
dijalankan. Mungkin itu bukan niat developer saat menambahkan setTimeout
tersebut ke myJob
.
Masukkan scheduler.yield()
, yang menempatkan kelanjutan fungsi apa pun yang memanggilnya dalam antrean prioritas yang sedikit lebih tinggi daripada memulai tugas serupa lainnya. Jika myJob
diubah untuk menggunakannya:
async function myJob() {
// Run part 1.
myJobPart1();
// Yield with scheduler.yield() to break up long task, then run part2.
await scheduler.yield();
myJobPart2();
}
Sekarang eksekusi akan terlihat seperti:
Browser masih memiliki peluang untuk menjadi responsif, tetapi sekarang kelanjutan tugas myJob
diprioritaskan daripada memulai tugas baru someoneElsesJob
, sehingga myJob
selesai sebelum someoneElsesJob
dimulai. Hal ini jauh lebih mendekati ekspektasi untuk memberikan thread utama untuk mempertahankan responsivitas, bukan melepaskan thread utama sepenuhnya.
Pewarisan prioritas
Sebagai bagian dari Prioritized Task Scheduling API yang lebih besar, scheduler.yield()
disusun dengan baik dengan prioritas eksplisit yang tersedia di scheduler.postTask()
. Tanpa prioritas yang ditetapkan secara eksplisit, scheduler.yield()
dalam callback scheduler.postTask()
pada dasarnya akan bertindak sama seperti contoh sebelumnya.
Namun, jika prioritas ditetapkan, seperti menggunakan prioritas 'background'
rendah:
async function lowPriorityJob() {
part1();
await scheduler.yield();
part2();
}
scheduler.postTask(lowPriorityJob, {priority: 'background'});
Lanjutan akan dijadwalkan dengan prioritas yang lebih tinggi daripada tugas 'background'
lainnya—mendapatkan lanjutan prioritas yang diharapkan sebelum tugas 'background'
yang tertunda—tetapi tetap memiliki prioritas yang lebih rendah daripada tugas default atau prioritas tinggi lainnya; tugas ini tetap merupakan tugas 'background'
.
Artinya, jika Anda menjadwalkan pekerjaan prioritas rendah dengan 'background'
scheduler.postTask()
(atau dengan requestIdleCallback
), kelanjutan setelah scheduler.yield()
di dalamnya juga akan menunggu hingga sebagian besar tugas lainnya selesai dan thread utama tidak ada aktivitas untuk dijalankan, yang persis seperti yang Anda inginkan dari menghasilkan tugas prioritas rendah.
Cara menggunakan API
Untuk saat ini, scheduler.yield()
hanya tersedia di browser berbasis Chromium. Jadi, untuk menggunakannya, Anda harus mendeteksi fitur dan kembali ke cara sekunder untuk menghasilkan browser lain.
scheduler-polyfill
adalah polyfill kecil untuk scheduler.postTask
dan scheduler.yield
yang secara internal menggunakan kombinasi metode untuk mengemulasi banyak kemampuan API penjadwalan di browser lain (meskipun pewarisan prioritas scheduler.yield()
tidak didukung).
Bagi mereka yang ingin menghindari polyfill, salah satu metodenya adalah menghasilkan menggunakan setTimeout()
dan menerima hilangnya kelanjutan yang diprioritaskan, atau bahkan tidak menghasilkan di browser yang tidak didukung jika hal itu tidak dapat diterima. Lihat dokumentasi scheduler.yield()
di Mengoptimalkan tugas yang lama untuk mengetahui informasi selengkapnya.
Jenis wicg-task-scheduling
juga dapat digunakan untuk mendapatkan pemeriksaan jenis dan dukungan IDE jika Anda mendeteksi fitur scheduler.yield()
dan menambahkan penggantian sendiri.
Pelajari lebih lanjut
Untuk mengetahui informasi selengkapnya tentang API dan cara API berinteraksi dengan prioritas tugas dan scheduler.postTask()
, lihat dokumen scheduler.yield()
dan Penjadwalan Tugas yang Diprioritaskan di MDN.
Untuk mempelajari lebih lanjut tugas yang lama, pengaruhnya terhadap pengalaman pengguna, dan tindakan yang harus dilakukan, baca artikel tentang mengoptimalkan tugas yang lama.