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

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

Untuk menghilangkan keharusan melakukan {i>trade-off<i} ini, Facebook mengusulkan dan menerapkan isInputPending() API di Chromium untuk meningkatkan responsivitas tanpa menghasilkan sesuatu. Berdasarkan masukan uji coba origin, kami telah melakukan sejumlah pembaruan pada API, dan dengan senang hati mengumumkan bahwa API tersebut 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 menunjukkan intent untuk mengirimkan API.

Latar belakang

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

Praktik terbaik saat ini adalah mengatasi masalah ini dengan melanggar JavaScript menjadi blok yang lebih kecil. Selagi halaman dimuat, halaman dapat menjalankan sedikit JavaScript, lalu menghasilkan dan meneruskan kontrol kembali ke browser. Tujuan {i>browser <i}kemudian dapat memeriksa antrean kejadian inputnya dan melihat apakah ada sesuatu yang perlu diberitahukan kepada halaman. Kemudian browser dapat kembali menjalankan Blok JavaScript saat ditambahkan. Hal ini membantu, tetapi dapat menyebabkan masalah lain.

Setiap kali laman mengembalikan kontrol ke {i>browser<i}, dibutuhkan beberapa waktu untuk browser untuk memeriksa antrean kejadian inputnya, memproses peristiwa, dan mengambil Pemblokiran JavaScript. Saat browser merespons kejadian lebih cepat, keseluruhan waktu pemuatan halaman akan melambat. Jika kita terlalu sering menyerah, halaman memuat terlalu lambat. Jika kami lebih jarang memberikan hasil, browser akan memerlukan waktu lebih lama untuk menanggapi kejadian pengguna, dan orang-orang menjadi frustrasi. Tidak seru.

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 segala sesuatu yang akan terjadi jika kami memiliki pendekatan baru untuk pemuatan yang akan menghilangkan {i>trade-off<i} yang menjengkelkan ini. Rab menghubungi teman-teman kami di Chrome tentang hal ini, dan mendapatkan proposal untuk isInputPending(). isInputPending() API adalah yang pertama menggunakan konsep menginterupsi input pengguna di web, dan memungkinkan JavaScript dapat memeriksa input tanpa bergantung 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 terhadap API tersebut, kami berpartner dengan kolega kami di Chrome untuk menerapkan dan mengirimkan fitur ini di Chromium. Dengan bantuan dari Chrome engineer, kami menerapkan patch pada uji coba origin (yang merupakan cara 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 dalam W3C Web Performance Working Group dan menerapkan perubahan pada API.

Contoh: penjadwal yang lebih produktif

Misalkan Anda memiliki banyak tugas pemblokiran tampilan yang harus dilakukan untuk memuat misalnya menghasilkan markup dari komponen, memfaktorkan bilangan prima, atau hanya menampilkan indikator lingkaran berputar pemuatan yang keren. Masing-masing ini dipecah menjadi item pekerjaan. Dengan menggunakan pola penjadwal, mari kita sketsa bagaimana kita mungkin memproses pekerjaan kita 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 agak responsif terhadap input (dapat menjalankan pengendali peristiwa sebelum pekerjaan dilanjutkan) sambil tetap mengelola untuk menjalankan tanpa gangguan. Meskipun demikian, kami mungkin mendapatkan jadwal waktu yang lama oleh pekerjaan lain yang menginginkan kontrol loop peristiwa, atau mendapatkan hingga QUANTUM milidetik tambahan latensi peristiwa.

Tidak apa-apa, tetapi bisakah kita melakukan 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 kepada navigator.scheduling.isInputPending(), kita dapat merespons input lebih cepat sambil tetap memastikan bahwa fungsi pemblokir tampilan kami tanpa gangguan. Jika kami tidak tertarik untuk menangani apa pun selain input (mis. mengecat) sampai pekerjaan selesai, kita dapat dengan mudah meningkatkan panjang QUANTUM juga.

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

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 membangun dukungan isInputPending() ke dalam library penjadwalan inti menggunakan logika serupa. Semoga, ini akan mengarahkan developer yang menggunakan framework ini agar dapat memperoleh manfaat dari isInputPending() di balik layar tanpa penulisan ulang yang signifikan.

Hasil tidak selalu buruk

Perlu dicatat bahwa menghasilkan lebih sedikit bukan solusi yang tepat untuk setiap penggunaan ini masalahnya atau bukan. Ada banyak alasan untuk mengembalikan kontrol ke {i>browser<i} selain untuk memproses peristiwa input, seperti melakukan rendering dan mengeksekusi skrip lain di pada halaman.

Ada kasus saat browser tidak dapat mengatribusikan dengan benar peristiwa input. Secara khusus, menyetel klip dan mask yang kompleks untuk lintas origin iframe mungkin melaporkan negatif palsu (yaitu isInputPending() mungkin ditampilkan secara tidak terduga false saat menargetkan frame ini). Pastikan bahwa Anda menghasilkan cukup sering jika situs Anda membutuhkan interaksi dengan subframe bergaya.

Perhatikan juga halaman lain yang berbagi loop peristiwa. Di platform seperti seperti Chrome untuk Android, cukup umum bagi beberapa origin untuk membagikan suatu peristiwa . isInputPending() tidak akan pernah menampilkan true jika input dikirim ke kerangka lintas asal, dan karenanya laman di latar belakang dapat mengganggu tingkat respons laman latar depan. Anda mungkin ingin mengurangi, menunda, atau menyerah lebih sering saat melakukan pekerjaan di latar belakang menggunakan Page Visibility API.

Sebaiknya gunakan isInputPending() dengan bijak. Jika tidak ada tugas yang memblokir pengguna harus dilakukan, lalu berbaik hatilah kepada orang lain pada loop peristiwa dengan menghasilkan lebih sering. Tugas yang berjalan lama bisa berbahaya.

Masukan

Kesimpulan

Kami senang isInputPending() diluncurkan, dan para developer dapat untuk mulai menggunakannya sekarang. API ini adalah pertama kalinya Facebook membuat API web baru dan membawanya dari inkubasi ide ke proposal standar hingga benar-benar pengiriman di browser. Kami ingin berterima kasih kepada semua orang yang telah membantu kami mencapai dan berikan sapaan khusus kepada semua orang di Chrome yang telah membantu kami menyempurnakan ide ini dan kirimkan!

Foto utama oleh will H McMahan di Buka pembuka.