Mengoptimalkan eksekusi JavaScript

JavaScript sering kali memicu perubahan visual. Kadang-kadang hal itu secara langsung melalui manipulasi gaya, dan kadang-kadang penghitungannya, yang akan mengakibatkan perubahan visual, seperti menelusuri atau mengurutkan data. JavaScript yang berjalan lama atau jelek pengaturan waktunya bisa menjadi penyebab umum masalah kinerja. Anda harus berusaha sebisa mungkin meminimalkan dampaknya.

JavaScript sering kali memicu perubahan visual. Terkadang hal itu langsung melalui manipulasi gaya, dan kadang-kadang penghitungannya yang akan mengakibatkan perubahan visual, seperti menelusuri atau mengurutkan data. JavaScript yang berjalan lama atau jelek pengaturan waktunya bisa menjadi penyebab umum masalah performa. Anda harus berusaha sebisa mungkin meminimalkan dampaknya.

Membuat profil performa JavaScript merupakan seni tersendiri, karena JavaScript yang Anda tulis bukanlah seperti kode yang sebenarnya dieksekusi. Browser modern menggunakan compiler JIT dan semua cara pengoptimalan dan trik untuk dicoba dan memberi Anda eksekusi yang secepat mungkin, dan ini pada dasarnya akan mengubah dinamika kode.

Namun, dengan semua hal yang telah disebutkan, ada beberapa hal yang pasti dapat Anda lakukan untuk membantu aplikasi mengeksekusi JavaScript dengan baik.

Ringkasan

  • Hindari setTimeout atau setInterval untuk pembaruan visual; sebagai gantinya gunakan selalu requestAnimationFrame.
  • Pindahkan JavaScript yang berjalan lama dari thread utama ke Web Worker.
  • Gunakan tugas mikro untuk membuat perubahan DOM melalui sejumlah bingkai.
  • Gunakan Timeline dan JavaScript Profiler di Chrome DevTools untuk menilai dampak JavaScript.

Menggunakan requestAnimationFrame untuk perubahan visual

Saat perubahan visual terjadi di layar, Anda ingin melakukan pekerjaan pada waktu yang tepat untuk browser, yaitu tepat saat memulai bingkai. Satu-satunya cara untuk memastikan JavaScript Anda akan berjalan di awal frame adalah menggunakan requestAnimationFrame.

/**
    * If run as a requestAnimationFrame callback, this
    * will be run at the start of the frame.
    */
function updateScreen(time) {
    // Make visual updates here.
}

requestAnimationFrame(updateScreen);

Framework atau contoh dapat menggunakan setTimeout atau setInterval untuk melakukan perubahan visual seperti animasi, tetapi masalahnya adalah callback akan berjalan pada beberapa titik dalam frame, mungkin tepat di akhir, dan sering kali pengaruhnya bisa menyebabkan kita kehilangan bingkai, sehingga mengakibatkan jank.

setTimeout menyebabkan browser kehilangan bingkai.

Bahkan, jQuery sebelumnya menggunakan setTimeout untuk perilaku animate-nya. Ini diubah untuk menggunakan requestAnimationFrame dalam versi 3. Jika menggunakan jQuery versi lama, Anda dapat menambalnya untuk menggunakan requestAnimationFrame, yang sangat dianjurkan.

Mengurangi kompleksitas atau menggunakan Web Workers

JavaScript berjalan di thread utama browser, persis selama penghitungan gaya, tata letak, dan, dalam banyak kasus, paint. Jika JavaScript Anda berjalan lama, ia akan memblokir tugas lain, sehingga dapat menyebabkan hilangnya bingkai.

Anda harus bersikap taktis tentang kapan JavaScript dijalankan, dan berapa lama. Misalnya, jika Anda berada dalam animasi seperti men-scroll, idealnya Anda harus terus mengamati JavaScript Anda melakukan sesuatu di wilayah 3-4 md. Bila lebih lama dari itu maka Anda berisiko menghabiskan waktu terlalu banyak. Jika Anda sedang dalam periode menganggur, Anda bisa lebih santai dengan waktu yang dihabiskan.

Dalam banyak kasus, Anda dapat memindahkan pekerjaan komputasi murni ke Web Workers, misalnya, jika tidak memerlukan akses DOM. Manipulasi data atau traversal, seperti pengurutan atau penelusuran, sering kali cocok untuk model ini, seperti pemuatan dan pembuatan model.

var dataSortWorker = new Worker("sort-worker.js");
dataSortWorker.postMesssage(dataToSort);

// The main thread is now free to continue working on other things...

dataSortWorker.addEventListener('message', function(evt) {
    var sortedData = evt.data;
    // Update data on screen...
});

Tidak semua pekerjaan cocok dengan model ini: Web Workers tidak memiliki akses DOM. Jika pekerjaan Anda harus berada di thread utama, pertimbangkan pendekatan batch, di mana Anda memecah tugas yang lebih besar menjadi tugas-tugas mikro, masing-masing memerlukan waktu tidak lebih dari beberapa milidetik, dan berjalan di dalam pengendali requestAnimationFrame di setiap frame.

Ada konsekuensi UX dan UI pada pendekatan ini, dan Anda perlu memastikan pengguna mengetahui bahwa tugas sedang diproses, baik dengan menggunakan indikator progres atau aktivitas. Setidak-tidaknya pendekatan ini akan membuat thread utama aplikasi Anda tetap bebas, sehingga membantunya tetap responsif terhadap interaksi pengguna.

Mengetahui “frame tax” JavaScript Anda

Saat menilai framework, library, atau kode Anda sendiri, kita perlu menilai biaya untuk menjalankan kode JavaScript bingkai-per-bingkai. Hal ini terutama penting saat melakukan pekerjaan animasi yang sangat membutuhkan kinerja seperti transisi atau scroll.

Panel Performance di Chrome DevTools adalah cara terbaik untuk mengukur biaya JavaScript Anda. Biasanya Anda akan mendapatkan catatan tingkat rendah seperti ini:

Rekaman performa di Chrome DevTools

Bagian Main menyediakan bagan api panggilan JavaScript sehingga Anda dapat menganalisis fungsi mana yang dipanggil dan berapa lama waktu yang diperlukan setiap fungsi.

Dengan informasi ini, Anda dapat menilai dampak performa JavaScript pada aplikasi Anda, dan mulai menemukan serta memperbaiki hotspot di mana fungsi perlu waktu terlalu lama untuk dieksekusi. Sebagaimana disebutkan sebelumnya, Anda harus berusaha menghapus JavaScript yang berjalan lama, atau, jika tidak memungkinkan, memindahkannya ke Web Worker sehingga membebaskan thread utama untuk melanjutkan tugas yang lain.

Lihat Memulai Analisis Performa Runtime untuk mempelajari cara menggunakan panel Performa.

Hindari melakukan pengoptimalan mikro pada JavaScript Anda

Mungkin menyenangkan untuk mengetahui bahwa browser dapat menjalankan satu versi suatu hal 100 kali lebih cepat daripada hal lain, seperti meminta offsetTop elemen lebih cepat daripada menghitung getBoundingClientRect(), tetapi hampir selalu benar bahwa Anda hanya akan memanggil fungsi seperti ini beberapa kali per frame, sehingga biasanya sia-sia untuk berfokus pada aspek performa JavaScript ini. Biasanya Anda hanya akan menghemat sepersekian milidetik.

Jika Anda sedang membuat game, atau aplikasi yang mahal secara komputasi, maka mungkin Anda merupakan pengecualian dari panduan ini, karena biasanya Anda akan mengepaskan banyak komputasi ke dalam satu bingkai, dan jika demikian barulah hal ini berguna.

Singkatnya, Anda harus sangat berhati-hati dengan pengoptimalan mikro karena biasanya itu tidak akan memetakan ke jenis aplikasi yang sedang Anda bangun.