Membuat situs yang merespons masukan pengguna dengan cepat telah menjadi salah satu aspek performa web yang paling menantang. Tim Chrome telah bekerja keras untuk membantu developer web memenuhinya. Baru pada tahun ini, diumumkan bahwa metrik Interaction to Next Paint (INP) akan beralih dari status eksperimental ke status tertunda. Aplikasi ini kini siap menggantikan First Input Delay (FID) sebagai Core Web Vitals pada Maret 2024.
Dalam upaya berkelanjutan untuk menghadirkan API baru yang membantu developer web membuat situs mereka secepat mungkin, Tim Chrome saat ini menjalankan uji coba origin untuk scheduler.yield
mulai Chrome versi 115. scheduler.yield
adalah penambahan baru yang diusulkan untuk API penjadwal yang memungkinkan cara yang lebih mudah dan lebih baik untuk menghasilkan kontrol kembali ke thread utama daripada metode yang secara tradisional diandalkan.
Saat menghasilkan
JavaScript menggunakan model run-to-completion untuk menangani tugas. Artinya, saat tugas berjalan di thread utama, tugas tersebut akan berjalan selama diperlukan untuk diselesaikan. Setelah tugas selesai, kontrol dihasilkan kembali ke thread utama, yang memungkinkan thread utama memproses tugas berikutnya dalam antrean.
Selain kasus ekstrem ketika tugas tidak pernah selesai, misalnya seperti loop tanpa henti, proses menghasilkan adalah aspek yang tidak terelakkan dari logika penjadwalan tugas JavaScript. Ini akan terjadi, hanya masalah kapan, dan lebih cepat lebih baik daripada nanti. Jika memerlukan waktu terlalu lama untuk dijalankan, tepatnya lebih dari 50 milidetik, tugas tersebut dianggap sebagai tugas yang panjang.
Tugas yang berjalan lama adalah sumber responsivitas halaman yang buruk, karena tugas tersebut menunda kemampuan browser untuk merespons input pengguna. Semakin sering tugas berlangsung—dan semakin lama dijalankan—semakin besar kemungkinan pengguna mendapatkan kesan bahwa halaman tersebut lambat, atau bahkan merasa halaman tersebut benar-benar rusak.
Namun, hanya karena kode Anda memulai tugas di browser, bukan berarti Anda harus menunggu hingga tugas tersebut selesai sebelum kontrol dikembalikan ke thread utama. Anda dapat meningkatkan responsivitas terhadap input pengguna pada halaman dengan menghasilkan tugas secara eksplisit, yang memecah tugas menjadi selesai pada peluang berikutnya yang tersedia. Hal ini memungkinkan tugas lain mendapatkan waktu di thread utama lebih cepat dibandingkan jika mereka harus menunggu tugas yang lama selesai.
Jika secara eksplisit menyerah, beri tahu browser "Hai, saya mengerti bahwa pekerjaan yang akan saya lakukan bisa memakan waktu cukup lama, dan saya tidak ingin Anda harus melakukan semua pekerjaan itu sebelum merespons input pengguna atau tugas lain yang mungkin juga penting". Ini adalah alat yang berharga dalam toolbox developer yang dapat sangat membantu meningkatkan pengalaman pengguna.
Masalah pada strategi hasil saat ini
Metode umum untuk menghasilkan menggunakan setTimeout
dengan nilai waktu tunggu 0
. Cara ini berfungsi karena callback yang diteruskan ke setTimeout
akan memindahkan pekerjaan yang tersisa ke tugas terpisah yang akan diantrekan untuk eksekusi berikutnya. Daripada menunggu browser memberikan hasil sendiri, Anda mengatakan "mari kita pecah bagian besar pekerjaan ini menjadi bagian-bagian kecil yang lebih kecil".
Namun, menghasilkan dengan setTimeout
memiliki efek samping yang mungkin tidak diinginkan: pekerjaan yang muncul setelah titik hasil akan berada di bagian belakang task queue. Tugas yang dijadwalkan oleh interaksi pengguna akan tetap berada di depan antrean seperti seharusnya—tetapi pekerjaan tersisa yang ingin Anda lakukan setelah menghasilkan secara eksplisit dapat lebih tertunda oleh tugas lain dari sumber pesaing yang telah mengantre sebelumnya.
Untuk melihat penerapannya, coba demo Glitch ini—atau lakukan eksperimen dengannya dalam versi yang disematkan di bawah. Demo ini terdiri dari beberapa tombol yang dapat Anda klik, dan kotak di bawahnya yang mencatat saat tugas dijalankan. Saat Anda membuka halaman tersebut, lakukan tindakan berikut:
- Klik tombol atas berlabel Jalankan tugas secara berkala, yang akan menjadwalkan tugas pemblokiran agar berjalan sesering mungkin. Saat Anda mengklik tombol ini, log tugas akan diisi dengan beberapa pesan yang bertuliskan Menjalankan tugas pemblokiran dengan
setInterval
. - Selanjutnya, klik tombol berlabel Run loop, menghasilkan dengan
setTimeout
pada setiap iterasi.
Anda akan melihat bahwa kotak di bagian bawah demo akan terbaca seperti ini:
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
Output ini menunjukkan perilaku "akhir task queue" yang terjadi saat menghasilkan dengan setTimeout
. Loop yang menjalankan pemrosesan lima item dan menghasilkan setTimeout
setelah setiap item diproses.
Hal ini menggambarkan masalah umum di web: Hal ini bukanlah hal yang tidak biasa bagi skrip—terutama skrip pihak ketiga—untuk mendaftarkan fungsi timer yang berjalan pada interval tertentu. Perilaku "akhir task queue" yang muncul saat menghasilkan dengan setTimeout
berarti bahwa pekerjaan dari sumber tugas lain dapat diantrekan sebelum pekerjaan tersisa yang harus dilakukan loop setelah menghasilkan.
Bergantung pada aplikasi Anda, hal ini mungkin atau mungkin tidak menjadi hasil yang diinginkan—tetapi dalam banyak kasus, perilaku inilah yang menyebabkan developer mungkin merasa enggan untuk menyerahkan kontrol atas thread utama dengan begitu cepat. Hasil yang dihasilkan bagus karena interaksi pengguna memiliki peluang untuk berjalan lebih cepat, tetapi juga memungkinkan pekerjaan interaksi non-pengguna lainnya untuk mendapatkan waktu di thread utama. Masalah ini nyata—tetapi scheduler.yield
dapat membantu menyelesaikannya.
Masukkan scheduler.yield
scheduler.yield
telah tersedia di belakang tanda sebagai fitur platform web eksperimental sejak Chrome versi 115. Satu pertanyaan yang mungkin Anda miliki adalah "mengapa saya memerlukan fungsi khusus untuk menghasilkan padahal setTimeout
sudah melakukannya?"
Perlu diperhatikan bahwa menghasilkan bukanlah sasaran desain setTimeout
, melainkan efek samping yang bagus dalam penjadwalan callback untuk dijalankan pada lain waktu di masa mendatang—bahkan dengan nilai waktu tunggu 0
yang ditentukan. Namun, hal yang lebih penting untuk diingat adalah menghasilkan dengan setTimeout
akan mengirimkan pekerjaan yang tersisa ke belakang task queue. Secara default, scheduler.yield
mengirimkan pekerjaan yang tersisa ke depan antrean. Ini berarti bahwa pekerjaan yang ingin Anda lanjutkan segera setelah memberikan hasil tidak akan mengabaikan tugas dari sumber lain (dengan pengecualian penting terkait interaksi pengguna).
scheduler.yield
adalah fungsi yang menghasilkan thread utama dan menampilkan Promise
saat dipanggil. Artinya, Anda dapat await
dalam fungsi async
:
async function yieldy () {
// Do some work...
// ...
// Yield!
await scheduler.yield();
// Do some more work...
// ...
}
Untuk melihat cara kerja scheduler.yield
, lakukan hal berikut:
- Buka
chrome://flags
- Aktifkan eksperimen fitur Platform Web Eksperimental. Anda mungkin harus memulai ulang Chrome setelah melakukannya.
- Buka halaman demo atau gunakan versi sematannya di bawah daftar ini.
- Klik tombol atas berlabel Jalankan tugas secara berkala.
- Terakhir, klik tombol berlabel Run loop, menghasilkan dengan
scheduler.yield
pada setiap iterasi.
Output di kotak di bagian bawah halaman akan terlihat seperti ini:
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
Tidak seperti demo yang menghasilkan penggunaan setTimeout
, Anda dapat melihat bahwa loop—meskipun menghasilkan setelah setiap iterasi—tidak mengirimkan tugas yang tersisa ke bagian belakang antrean, tetapi ke depannya. Hal ini akan memberikan yang terbaik dari kedua hal tersebut: Anda dapat menghasilkan untuk meningkatkan responsivitas input di situs Anda, tetapi juga memastikan bahwa pekerjaan yang ingin Anda selesaikan setelah hasil tidak tertunda.
Cobalah!
Jika scheduler.yield
tampak menarik bagi Anda dan Anda ingin mencobanya, Anda dapat melakukannya dengan dua cara mulai Chrome versi 115:
- Jika Anda ingin bereksperimen dengan
scheduler.yield
secara lokal, ketik dan masukkanchrome://flags
di kolom URL Chrome, lalu pilih Enable dari dropdown di bagian Experimental Web Platform Features. Ini akan membuatscheduler.yield
(dan fitur eksperimental lainnya) hanya tersedia di instance Chrome Anda. - Jika ingin mengaktifkan
scheduler.yield
untuk pengguna Chromium sebenarnya dengan origin yang dapat diakses secara publik, Anda harus mendaftar ke uji coba originscheduler.yield
. Hal ini memungkinkan Anda bereksperimen secara aman dengan fitur yang diusulkan selama jangka waktu tertentu, dan memberikan insight berharga kepada Tim Chrome tentang cara fitur tersebut digunakan di lapangan. Untuk mengetahui informasi selengkapnya tentang cara kerja uji coba origin, baca panduan ini.
Cara penggunaan scheduler.yield
—sambil tetap mendukung browser yang tidak menerapkannya—bergantung pada sasaran Anda. Anda dapat menggunakan polyfill resmi. Polyfill berguna jika hal berikut berlaku untuk situasi Anda:
- Anda sudah menggunakan
scheduler.postTask
di aplikasi untuk menjadwalkan tugas. - Anda ingin dapat menetapkan tugas dan prioritas hasil.
- Anda ingin dapat membatalkan atau memprioritaskan ulang tugas melalui class
TaskController
yang ditawarkanscheduler.postTask
API.
Jika ini tidak menggambarkan situasi Anda, maka polyfill mungkin tidak untuk Anda. Dalam hal ini, Anda dapat melempar penggantian sendiri dengan beberapa cara. Pendekatan pertama menggunakan scheduler.yield
jika tersedia, tetapi kembali ke setTimeout
jika tidak tersedia:
// 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:
// ...
}
Tindakan ini dapat berfungsi, tetapi seperti yang Anda duga, browser yang tidak mendukung scheduler.yield
akan menghasilkan tanpa perilaku "front of queue". Jika itu berarti Anda memilih tidak menghasilkan sama sekali, Anda dapat mencoba pendekatan lain yang menggunakan scheduler.yield
jika tersedia, tetapi tidak akan menghasilkan sama sekali jika tidak tersedia:
// 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
adalah tambahan yang menarik untuk API penjadwal, yang diharapkan akan memudahkan developer untuk meningkatkan responsivitas daripada strategi hasil saat ini. Jika scheduler.yield
sepertinya API yang berguna bagi Anda, berpartisipasilah dalam riset kami untuk membantu meningkatkannya, dan berikan masukan tentang bagaimana API tersebut dapat ditingkatkan.
Banner besar dari Unsplash, oleh Jonathan Allison.