Cara JavaScript membaca dan menjalankan kode memengaruhi kecepatan program sekaligus batasan penulisannya. Topik ini mengulas engine yang mengeksekusi kode, jalur dari kode sumber sampai kode mesin, lalu model satu thread yang menjadi gambaran mental dasar. Bagian akhirnya menunjukkan implikasi praktis dari model tersebut, yaitu kode berurutan yang berjalan lama menahan pekerjaan lain.
Engine yang mengeksekusi kode#
Engine adalah program yang membaca teks kode JavaScript lalu menjalankannya, dan tanpa engine kode itu hanya teks biasa. Engine pertama dibuat oleh Brendan Eich pada 1995 untuk Netscape, dan turunannya yang bernama SpiderMonkey masih dipakai Firefox sampai sekarang. Chrome dan Edge memakai V8, engine buatan Google yang juga dibawa Node.js, sedangkan Safari memakai JavaScriptCore. Meski pembuatnya berbeda, semua engine mengikuti spesifikasi ECMAScript, sehingga kode yang sama dapat berjalan di semuanya. Mengenal nama engine membantu saat membaca dokumentasi performa atau berita fitur baru, karena banyak penjelasan teknis menyebutnya.
Pekerjaan engine tidak berhenti pada mengeksekusi perintah. Engine memeriksa keabsahan sintaks sebelum kode berjalan, mengelola memori untuk objek yang dibuat program, dan mengoptimalkan bagian kode yang sering dipakai. Optimalisasi inilah yang membuat JavaScript dapat berkecepatan tinggi pada engine modern, meski bahasa ini pada awalnya ditafsirkan.
Dari kode sumber menuju kode mesin#
Engine modern mengolah kode melalui beberapa tahap sebelum perintahnya benar-benar dijalankan. Tahap pertama adalah parsing, proses membaca teks kode dan menyusunnya menjadi AST (abstract syntax tree), pohon yang menggambarkan struktur program. Tahap berikutnya, interpreter menerjemahkan pohon itu menjadi bytecode, yakni bentuk perantara yang lebih cepat dieksekusi daripada teks aslinya. Selama program berjalan, engine mengamati bagian mana yang paling sering dieksekusi. Bagian yang sering itu disebut jalur panas (hot path), contohnya fungsi yang dipanggil ribuan kali di dalam perulangan.
Untuk jalur panas, engine memakai kompilasi just-in-time (JIT), yakni teknik mengompilasi kode menjadi kode mesin saat program berjalan, bukan sebelum dijalankan. Kode mesin berisi instruksi yang dipahami langsung oleh prosesor, sehingga eksekusinya jauh lebih cepat daripada menafsirkan bytecode yang sama berulang kali. Engine juga dapat mengoptimalkan jalur panas berdasarkan pola pemakaiannya, misalnya dengan berasumsi tipe data tertentu selalu sama. Bila asumsi itu meleset, engine membuang hasil optimalisasinya dan kembali ke jalur bytecode. Perpindahan ini tidak pernah diketahui oleh program yang berjalan.
Alur lengkapnya dapat dilihat pada diagram berikut.
Rangkaian interpretasi dan JIT ini menjelaskan dua hal sekaligus. Program JavaScript dapat dimulai segera karena engine langsung menjalankan bytecode tanpa menunggu seluruh program dikompilasi lebih dahulu. Kode yang dipanggil berulang kali justru makin cepat, karena JIT mendapat kesempatan mengenal pola pemakaiannya lalu mengompilasinya menjadi kode mesin.
Satu thread dan event loop sebagai gambaran awal#
JavaScript menjalankan kode dalam satu thread, yakni satu urutan kerja yang memproses satu perintah pada satu waktu. Dalam model ini, perintah kedua baru dimulai setelah perintah pertama benar-benar selesai, dan tidak ada dua potongan kode yang berjalan serentak. Pengaturan gilirannya ditangani event loop, yakni mekanisme yang mengambil pekerjaan berikutnya dari antrean saat thread sedang kosong. Fungsi yang berjalan sepuluh detik menahan semua kode lain selama sepuluh detik penuh, karena threadnya sedang sibuk.
Gambaran di atas sengaja dibuat sederhana. Detail penuh event loop, mulai dari antrean tugas hingga penjadwalannya, dibahas tersendiri pada modul asinkron. Untuk saat ini, satu thread yang memproses kode satu per satu sudah cukup menjadi fondasi, dan implikasi praktisnya langsung terlihat pada contoh di bawah.
Banyak bahasa lain menyediakan beberapa thread sekaligus, sehingga dua bagian program dapat benar-benar berjalan bersamaan. Model dasar JavaScript tidak menawarkan jalur paralel seperti itu. Lingkungan JavaScript tetap menyediakan cara terpisah untuk pekerjaan berat yang benar-benar paralel, seperti Web Worker di browser dan worker thread di Node.js, tetapi keduanya berada di luar model dasar ini. Untuk sebagian besar program awal, satu antrean yang diproses satu per satu adalah mental model yang tepat.
Kode berurutan yang lama memblokir#
Cara paling langsung memahami model ini adalah melihat urutan eksekusinya. Program berikut memanggil satu fungsi dua kali dan mencetak tanda mulai serta selesai, semuanya diproses dari atas ke bawah tanpa jeda.
// kode sinkron berjalan persis dari atas ke bawah
function sapa(nama) {
console.log(`Halo, ${nama}!`);
}
console.log("Program dimulai");
sapa("Citra");
sapa("Budi");
console.log("Program berakhir");Program dimulai
Halo, Citra!
Halo, Budi!
Program berakhirModel satu thread menjadi terasa saat ada pekerjaan yang berjalan lama. Perulangan berikut menjumlahkan angka satu sampai satu juta, dan selama perhitungan itu berlangsung tidak ada baris kode lain yang sempat dieksekusi.
// perulangan panjang menahan semua pekerjaan lain hingga selesai
let jumlah = 0;
for (let i = 1; i <= 1000000; i++) {
jumlah += i;
}
console.log(`Jumlah 1 sampai 1000000 = ${jumlah}`);
console.log("Baris ini baru tampil setelah perulangan selesai");Jumlah 1 sampai 1000000 = 500000500000
Baris ini baru tampil setelah perulangan selesaiDi dalam Node.js, kode sesudah perulangan baru berjalan setelah perhitungan selesai. Di dalam browser, efeknya jauh lebih terlihat karena antarmuka halaman ikut menunggu. Klik dari pengguna, ketikan pada form, dan animasi yang sedang berjalan semuanya harus antre sampai kode berurutan itu selesai.
Kondisi inilah yang menjadi alasan JavaScript mengembangkan cara kerja asinkron, yakni pendekatan yang memindahkan pekerjaan panjang keluar dari jalur utama. Dengan begitu, thread yang sama tetap bebas menanggapi interaksi pengguna, sementara pekerjaan panjangnya berjalan di latar belakang. Semakin panjang kode berurutannya, semakin lama pula halaman tidak dapat melayani apa pun.
Ringkasan#
- Engine seperti V8, SpiderMonkey, dan JavaScriptCore membaca kode sumber lalu menjalankannya sesuai spesifikasi ECMAScript.
- Eksekusi modern melewati parsing menjadi AST, interpretasi menjadi bytecode, lalu kompilasi JIT untuk jalur panas.
- JavaScript berjalan dalam satu thread dengan event loop sebagai pengatur giliran, sehingga perintah diproses satu per satu.
- Kode berurutan yang berjalan lama memblokir pekerjaan lain, dan di browser efeknya berupa halaman yang membeku.
Referensi#
- JavaScript — MDN Web Docs — Dokumentasi induk JavaScript, termasuk ikhtisar bahasa dan perilaku eksekusinya.
- The Modern JavaScript Tutorial — javascript.info — Tutorial menyeluruh yang mencakup dasar bahasa sampai topik lanjutan seperti engine dan event loop.