Memperkenalkan uji coba origin scheduler.results

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.

Penggambaran tentang bagaimana memecah tugas bisa memfasilitasi responsivitas input yang lebih baik. Di bagian atas, tugas yang panjang memblokir pengendali peristiwa agar tidak berjalan hingga tugas selesai. Di bagian bawah, tugas yang dibagi-bagi memungkinkan pengendali peristiwa berjalan lebih cepat dari yang seharusnya.
Visualisasi yang menghasilkan kontrol kembali ke thread utama. Di bagian atas, proses menghasilkan hanya terjadi setelah tugas berjalan hingga selesai, yang berarti tugas dapat memerlukan waktu lebih lama untuk diselesaikan sebelum mengembalikan kontrol kembali ke thread utama. Di bagian bawah, hasil dilakukan secara eksplisit, memecah tugas yang panjang menjadi beberapa tugas yang lebih kecil. Hal ini memungkinkan interaksi pengguna berjalan lebih cepat, yang meningkatkan responsivitas input dan INP.

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:

  1. 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.
  2. 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:

  1. Buka chrome://flags
  2. Aktifkan eksperimen fitur Platform Web Eksperimental. Anda mungkin harus memulai ulang Chrome setelah melakukannya.
  3. Buka halaman demo atau gunakan versi sematannya di bawah daftar ini.
  4. Klik tombol atas berlabel Jalankan tugas secara berkala.
  5. 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:

  1. Jika Anda ingin bereksperimen dengan scheduler.yield secara lokal, ketik dan masukkan chrome://flags di kolom URL Chrome, lalu pilih Enable dari dropdown di bagian Experimental Web Platform Features. Ini akan membuat scheduler.yield (dan fitur eksperimental lainnya) hanya tersedia di instance Chrome Anda.
  2. Jika ingin mengaktifkan scheduler.yield untuk pengguna Chromium sebenarnya dengan origin yang dapat diakses secara publik, Anda harus mendaftar ke uji coba origin scheduler.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:

  1. Anda sudah menggunakan scheduler.postTask di aplikasi untuk menjadwalkan tugas.
  2. Anda ingin dapat menetapkan tugas dan prioritas hasil.
  3. Anda ingin dapat membatalkan atau memprioritaskan ulang tugas melalui class TaskController yang ditawarkan scheduler.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.