Dipublikasikan: 6 Maret 2025
Halaman terasa lambat dan tidak responsif saat tugas panjang membuat thread utama sibuk, sehingga tidak dapat melakukan pekerjaan penting lainnya, seperti merespons input pengguna. Akibatnya, bahkan kontrol formulir bawaan dapat tampak rusak bagi pengguna—seolah-olah halaman dibekukan—belum lagi komponen kustom yang lebih kompleks.
scheduler.yield()
adalah cara untuk menyerahkan tugas ke thread utama—memungkinkan browser menjalankan tugas penting yang tertunda—lalu melanjutkan eksekusi dari tempat terakhir. Hal ini membuat halaman lebih responsif dan, pada gilirannya, membantu meningkatkan Interaction to Next Paint (INP).
scheduler.yield
menawarkan API ergonomis yang melakukan persis seperti yang dikatakannya: eksekusi fungsi yang dipanggilnya akan dijeda pada ekspresi await scheduler.yield()
dan menghasilkan thread utama, sehingga memecah tugas. Eksekusi fungsi lainnya—yang 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 khusus scheduler.yield
adalah bahwa kelanjutan setelah yield dijadwalkan untuk berjalan sebelum menjalankan tugas serupa lainnya yang telah dimasukkan ke antrean oleh halaman. Hal ini memprioritaskan kelanjutan tugas daripada memulai tugas baru.
Fungsi seperti setTimeout
atau scheduler.postTask
juga dapat digunakan untuk memecah tugas, tetapi kelanjutan tersebut biasanya berjalan setelah tugas baru yang sudah diantrekan, sehingga berpotensi menimbulkan penundaan yang lama antara penyerahan ke thread utama dan penyelesaian tugas.
Lanjutan yang diprioritaskan setelah menghasilkan
scheduler.yield
adalah bagian dari Prioritized Task Scheduling API. Sebagai developer web, kita biasanya tidak membahas urutan tugas yang dijalankan oleh loop peristiwa 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 yang diantrekan dengan setTimeout(callback, 0)
.
Penjadwalan Tugas yang Diprioritaskan hanya membuatnya lebih eksplisit, sehingga lebih mudah untuk mengetahui tugas mana 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 beralih ke tugas lain. Jika tugas adalah kode yang berperilaku baik yang secara berkala menghasilkan 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 contohnya: dua fungsi, diantrekan untuk dijalankan dalam tugas yang berbeda menggunakan setTimeout
.
setTimeout(myJob);
setTimeout(someoneElsesJob);
Dalam kasus ini, dua 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 secara independen menyiapkan tugas untuk dijalankan, atau bisa juga dua tugas dari komponen terpisah yang dipicu jauh di dalam penjadwal framework Anda.
Berikut tampilan pekerjaan tersebut di DevTools:
myJob
ditandai sebagai tugas panjang, yang memblokir browser untuk melakukan hal lain saat sedang berjalan. Dengan asumsi berasal dari skrip pihak pertama, kita dapat memecahnya:
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
dijadwalkan, berikut tampilan eksekusinya:
Kita telah memecah tugas dengan setTimeout
sehingga browser dapat responsif selama myJob
, tetapi sekarang bagian kedua myJob
hanya berjalan setelah someoneElsesJob
selesai.
Dalam beberapa kasus, hal itu mungkin tidak masalah, tetapi biasanya hal ini tidak optimal. myJob
menyerahkan tugas 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. Hal itu mungkin bukan maksud 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 eksekusinya akan terlihat seperti:
Browser masih memiliki peluang untuk responsif, tetapi kini kelanjutan tugas myJob
diprioritaskan daripada memulai tugas baru someoneElsesJob
, sehingga myJob
selesai sebelum someoneElsesJob
dimulai. Hal ini jauh lebih mendekati ekspektasi untuk menyerahkan ke thread utama guna 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 yang diprioritaskan seperti yang diharapkan sebelum pekerjaan 'background'
yang tertunda—tetapi masih memiliki prioritas yang lebih rendah daripada tugas default atau prioritas tinggi lainnya; tugas ini tetap merupakan pekerjaan 'background'
.
Artinya, jika Anda menjadwalkan tugas berprioritas rendah dengan 'background'
scheduler.postTask()
(atau dengan requestIdleCallback
), kelanjutan setelah scheduler.yield()
di dalamnya juga akan menunggu hingga sebagian besar tugas lain selesai dan thread utama tidak ada aktivitas untuk dijalankan, yang persis seperti yang Anda inginkan dari penangguhan dalam tugas berprioritas rendah.
Cara menggunakan API
Untuk saat ini, scheduler.yield()
hanya tersedia di browser berbasis Chromium. Jadi, untuk menggunakannya, Anda harus mendeteksi fitur dan melakukan penggantian ke cara sekunder untuk memberikan hasil bagi 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 yang ingin menghindari polyfill, salah satu metode adalah dengan melakukan yield menggunakan setTimeout()
dan menerima hilangnya kelanjutan yang diprioritaskan, atau bahkan tidak melakukan yield di browser yang tidak didukung jika hal itu tidak dapat diterima. Lihat dokumentasi scheduler.yield()
di bagian Mengoptimalkan tugas panjang 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 interaksinya dengan prioritas tugas dan scheduler.postTask()
, lihat dokumen scheduler.yield()
dan Prioritized Task Scheduling di MDN.
Untuk mempelajari lebih lanjut tugas panjang, pengaruhnya terhadap pengalaman pengguna, dan tindakan yang harus dilakukan, baca artikel tentang mengoptimalkan tugas panjang.