Penjadwalan JS yang lebih baik dengan isInputMenunggu()

JavaScript API baru yang dapat membantu Anda menghindari kompromi antara performa pemuatan dan responsivitas input.

Nate Schloss
Nate Schloss
Andrew Comminos
Andrew Comminos

Memuat dengan cepat itu sulit. Situs yang memanfaatkan JS untuk merender kontennya saat ini harus melakukan kompromi antara performa pemuatan dan respons input: melakukan semua pekerjaan yang diperlukan untuk menampilkan sekaligus (performa pemuatan yang lebih baik, responsivitas input yang lebih buruk), atau membagi pekerjaan menjadi tugas yang lebih kecil agar tetap responsif terhadap input dan penggambaran (performa pemuatan yang lebih buruk, responsivitas input yang lebih baik).

Untuk menghilangkan kebutuhan untuk melakukan kompromi ini, Facebook mengusulkan dan menerapkan isInputPending() API di Chromium untuk meningkatkan responsivitas tanpa memberikan hasil. Berdasarkan masukan uji coba origin, kami telah melakukan sejumlah update pada API, dan dengan senang hati mengumumkan bahwa API ini kini dikirimkan secara default di Chromium 87.

Kompatibilitas browser

Dukungan Browser

  • Chrome: 87.
  • Edge: 87.
  • Firefox: tidak didukung.
  • Safari: tidak didukung.

Sumber

isInputPending() dikirim di browser berbasis Chromium mulai versi 87. Tidak ada browser lain yang telah memberikan sinyal untuk mengirimkan API.

Latar belakang

Sebagian besar pekerjaan di ekosistem JS saat ini dilakukan pada satu thread: thread utama. Hal ini memberikan model eksekusi yang andal bagi developer, tetapi pengalaman pengguna (khususnya responsivitas) dapat menurun secara drastis jika skrip dieksekusi dalam waktu yang lama. Jika halaman melakukan banyak pekerjaan saat peristiwa input diaktifkan, misalnya, halaman tidak akan menangani peristiwa input klik hingga setelah pekerjaan tersebut selesai.

Praktik terbaik saat ini adalah menangani masalah ini dengan membagi JavaScript menjadi beberapa blok yang lebih kecil. Saat halaman dimuat, halaman dapat menjalankan sedikit JavaScript, lalu memberikan dan meneruskan kontrol kembali ke browser. Browser kemudian dapat memeriksa antrean peristiwa inputnya dan melihat apakah ada hal yang perlu diberi tahu ke halaman. Kemudian, browser dapat kembali menjalankan blok JavaScript saat ditambahkan. Hal ini membantu, tetapi dapat menyebabkan masalah lain.

Setiap kali halaman memberikan kontrol kembali ke browser, browser memerlukan waktu beberapa saat untuk memeriksa antrean peristiwa inputnya, memproses peristiwa, dan mengambil blok JavaScript berikutnya. Meskipun browser merespons peristiwa lebih cepat, waktu pemuatan halaman secara keseluruhan akan melambat. Jika kita terlalu sering mengerjakannya, halaman akan dimuat terlalu lambat. Jika kita lebih jarang memberikan hasil, browser akan memerlukan waktu lebih lama untuk merespons peristiwa pengguna, dan orang-orang akan merasa frustrasi. Tidak menyenangkan.

Diagram yang menunjukkan bahwa saat Anda menjalankan tugas JS yang lama, browser memiliki lebih sedikit waktu untuk mengirim peristiwa.

Di Facebook, kami ingin melihat seperti apa tampilannya jika kami menemukan pendekatan baru untuk pemuatan yang akan menghilangkan kompromi yang menjengkelkan ini. Kami menghubungi teman-teman kami di Chrome tentang hal ini, dan mengajukan proposal untuk isInputPending(). isInputPending() API adalah yang pertama menggunakan konsep gangguan untuk input pengguna di web, dan memungkinkan JavaScript dapat memeriksa input tanpa menyerah pada browser.

Diagram yang menunjukkan bahwa isInputPending() memungkinkan JS Anda memeriksa apakah ada input pengguna yang tertunda, tanpa sepenuhnya menghasilkan eksekusi kembali ke browser.

Karena ada minat pada API ini, kami berpartner dengan rekan-rekan kami di Chrome untuk menerapkan dan mengirimkan fitur ini di Chromium. Dengan bantuan dari engineer Chrome, kami mendapatkan patch di balik uji coba origin (yang merupakan cara bagi Chrome untuk menguji perubahan dan mendapatkan masukan dari developer sebelum merilis API sepenuhnya).

Sekarang kami telah menerima masukan dari uji coba origin dan dari anggota lain W3C Web Performance Working Group serta menerapkan perubahan pada API.

Contoh: penjadwal yieldier

Misalkan Anda memiliki banyak pekerjaan pemblokiran tampilan yang harus dilakukan untuk memuat halaman, misalnya membuat markup dari komponen, memfaktorkan bilangan prima, atau hanya menggambar indikator lingkaran berputar pemuatan yang keren. Masing-masing dibagi menjadi item pekerjaan terpisah. Dengan pola penjadwal, mari kita buat sketsa cara memproses pekerjaan dalam fungsi processWorkQueue() fiktif:

const DEADLINE = performance.now() + QUANTUM;
while (workQueue.length > 0) {
 
if (performance.now() >= DEADLINE) {
   
// Yield the event loop if we're out of time.
    setTimeout
(processWorkQueue);
   
return;
 
}
  let job
= workQueue.shift();
  job
.execute();
}

Dengan memanggil processWorkQueue() nanti dalam macrotask baru melalui setTimeout(), kita memberi browser kemampuan untuk tetap responsif terhadap input (browser dapat menjalankan pengendali peristiwa sebelum pekerjaan dilanjutkan) sambil tetap berhasil berjalan tanpa gangguan. Meskipun demikian, kami mungkin dibatalkan jadwalnya dalam waktu lama oleh pekerjaan lain yang menginginkan kontrol atas loop peristiwa, atau mendapatkan latensi peristiwa tambahan hingga QUANTUM milidetik.

Ini sudah bagus, tetapi bisakah kita melakukan yang lebih baik? Tentu saja!

const DEADLINE = performance.now() + QUANTUM;
while (workQueue.length > 0) {
 
if (navigator.scheduling.isInputPending() || performance.now() >= DEADLINE) {
   
// Yield if we have to handle an input event, or we're out of time.
    setTimeout
(processWorkQueue);
   
return;
 
}
  let job
= workQueue.shift();
  job
.execute();
}

Dengan memperkenalkan panggilan ke navigator.scheduling.isInputPending(), kita dapat merespons input lebih cepat sambil tetap memastikan bahwa pekerjaan pemblokir tampilan dijalankan tanpa gangguan. Jika kita tidak tertarik untuk menangani apa pun selain input (misalnya melukis) hingga pekerjaan selesai, kita juga dapat dengan mudah menambah panjang QUANTUM.

Secara default, peristiwa "berkelanjutan" tidak ditampilkan dari isInputPending(). Ini meliputi mousemove, pointermove, dan lainnya. Jika Anda juga tertarik untuk menghasilkannya, tidak masalah. Dengan memberikan objek ke isInputPending() dengan includeContinuous disetel ke true, kita siap:

const DEADLINE = performance.now() + QUANTUM;
const options = { includeContinuous: true };
while (workQueue.length > 0) {
 
if (navigator.scheduling.isInputPending(options) || performance.now() >= DEADLINE) {
   
// Yield if we have to handle an input event (any of them!), or we're out of time.
    setTimeout
(processWorkQueue);
   
return;
 
}
  let job
= workQueue.shift();
  job
.execute();
}

Selesai. Framework seperti React mem-build dukungan isInputPending() ke dalam library penjadwalan intinya menggunakan logika serupa. Semoga hal ini akan membuat developer yang menggunakan framework ini dapat memanfaatkan isInputPending() di balik layar tanpa penulisan ulang yang signifikan.

Menyerah tidak selalu buruk

Perlu diperhatikan bahwa menghasilkan lebih sedikit bukanlah solusi yang tepat untuk setiap kasus penggunaan. Ada banyak alasan untuk mengembalikan kontrol ke browser selain untuk memproses peristiwa input, seperti untuk melakukan rendering dan menjalankan skrip lain di halaman.

Ada kasus saat browser tidak dapat mengatribusikan peristiwa input yang tertunda dengan benar. Secara khusus, menetapkan klip dan mask yang kompleks untuk iframe lintas-origin dapat melaporkan negatif palsu (yaitu isInputPending() dapat tiba-tiba menampilkan salah saat menargetkan frame ini). Pastikan Anda cukup sering menghasilkan jika situs Anda memerlukan interaksi dengan subframe bergaya.

Perhatikan juga halaman lain yang memiliki loop peristiwa yang sama. Di platform seperti Chrome untuk Android, beberapa origin sering kali berbagi loop peristiwa. isInputPending() tidak akan pernah menampilkan true jika input dikirim ke frame lintas origin, sehingga halaman latar belakang dapat mengganggu responsivitas halaman latar depan. Anda mungkin ingin mengurangi, menunda, atau menghasilkan lebih sering saat melakukan pekerjaan di latar belakang menggunakan Page Visibility API.

Sebaiknya gunakan isInputPending() dengan bijak. Jika tidak ada tugas pemblokiran pengguna yang harus dilakukan, bersikaplah baik kepada orang lain di loop peristiwa dengan memberikan lebih sering. Tugas yang berjalan lama bisa berbahaya.

Masukan

  • Berikan masukan tentang spesifikasi di repositori is-input-pending.
  • Hubungi @acomminos (salah satu penulis spesifikasi) di Twitter.

Kesimpulan

Kami senang isInputPending() diluncurkan, dan developer dapat mulai menggunakannya sekarang. API ini adalah pertama kalinya Facebook membangun API web baru dan mengembangkannya dari inkubasi ide hingga proposal standar hingga benar-benar dikirim di browser. Kami ingin berterima kasih kepada semua orang yang telah membantu kami sampai ke tahap ini, dan memberikan ucapan khusus kepada semua orang di Chrome yang telah membantu kami mengembangkan ide ini dan mengirimkannya.

Foto hero oleh Will H McMahan di Unsplash.