Event Loop & Urutan Eksekusi
Memahami cara Node menangani pekerjaan asinkron: call stack, microtask, nextTick, timer, dan setImmediate.
JavaScript: satu thread, satu pekerjaan
Kode JavaScript berjalan di satu thread dan mengerjakan satu hal pada satu waktu. Lalu bagaimana Node bisa melayani ribuan pengguna sekaligus? Jawabannya: ia tidak menunggu. Operasi lambat (jaringan, disk, timer) diserahkan ke sistem, dan hasilnya ditangani nanti lewat event loop.
Analogi kasir kopi
Barista (thread tunggal) menerima pesanan, menyalakan mesin espresso (operasi lambat), dan tidak berdiri menunggu: ia langsung melayani pelanggan berikutnya. Saat mesin berbunyi (selesai), barista menyelesaikan pesanan itu. Itulah non-blocking I/O.
Komponen utama
- Call stack: tempat fungsi yang sedang dijalankan.
- Antrian callback: tempat tugas yang sudah siap menunggu giliran.
- Event loop: pekerja yang terus memeriksa: "stack kosong? ada tugas siap? jalankan."
Urutan prioritas
Tidak semua tugas setara. Setelah kode sinkron selesai, Node menjalankan antrean dengan urutan:
- Kode sinkron (seluruh skrip utama).
process.nextTick()— antrean tertinggi.- Microtask: callback Promise (
.then,await),queueMicrotask. - Timers:
setTimeout/setIntervalyang waktunya tiba. - I/O callbacks: hasil baca file, jaringan.
setImmediate()— fase "check", setelah I/O.
Contoh klasik
console.log('1 sinkron');
setTimeout(() => console.log('5 setTimeout'), 0);
setImmediate(() => console.log('6 setImmediate'));
Promise.resolve().then(() => console.log('4 promise'));
process.nextTick(() => console.log('3 nextTick'));
console.log('2 sinkron akhir');
Keluaran: 1, 2, 3, 4, 5, 6. Perhatikan: setTimeout(fn, 0) bukan "jalankan sekarang" — callback-nya tetap menunggu semua kode sinkron dan microtask selesai.
Catatan: urutan setTimeout(…, 0) versus setImmediate di skrip utama tidak dijamin di Node asli (biasanya timeout dulu). Di dalam callback I/O, setImmediate selalu lebih dulu. Simulator KelasIT memakai urutan yang umum.
Dampak praktis: jangan blokir event loop
// MEMBLOKIR — semua pengguna lain menunggu 5 detik!
app.get('/hitung', (req, res) => {
const mulai = Date.now();
while (Date.now() - mulai < 5000) {} // perhitungan berat sinkron
res.send('selesai');
});
Pekerjaan CPU berat sebaiknya dipindah ke worker_threads atau proses terpisah. Pekerjaan I/O aman karena asinkron.
setTimeout dan setInterval
const t = setTimeout(() => console.log('sekali'), 1000);
clearTimeout(t); // batalkan sebelum jalan
const i = setInterval(() => console.log('berulang'), 500);
setTimeout(() => clearInterval(i), 2100); // hentikan interval
Waktu pada timer adalah minimum, bukan jaminan: jika event loop sedang sibuk, callback bisa terlambat.
Dua tipe penjadwalan "segera"
process.nextTick: sebelum Promise; berlebihan memakainya bisa "kelaparan" (starve) I/O.queueMicrotask/ Promise: setelah nextTick, sebelum timer.setImmediate: setelah fase I/O saat ini.