Cara JavaScript Berjalan: Engine, Event Loop, Async · 8/9
Microtask vs macrotask: memprediksi urutan output
Kenapa callback promise selalu menang dari setTimeout(0). Pertanyaan interview klasik, dijelaskan.
Baca 25 menit
Setelah pelajaran ini kamu bisa
- Menjelaskan bedanya microtask dan macrotask
- Memprediksi urutan output yang tepat dari campuran kode sinkron, promise, dan timer
- Menjelaskan kenapa callback promise selalu menang dari setTimeout(0)
- Mengenali microtask starvation
Queue-nya bukan satu — ada dua, dengan prioritas berbeda. Ini pelajaran yang mengubah pertanyaan interview terkenal itu dari hafalan jadi sesuatu yang bisa kamu pikirkan.
| Microtask | Macrotask | |
|---|---|---|
| Dibuat oleh | .then, await, queueMicrotask | setTimeout, setInterval, I/O, event |
| Prioritas | Lebih tinggi | Lebih rendah |
| Dikosongkan | Semuanya, sampai habis | Satu, lalu microtask lagi |
Algoritma sebenarnya dari loop-nya, berurutan: jalankan semua kode sinkron sampai stack kosong → kosongkan setiap microtask, termasuk yang ditambahkan saat proses pengosongan → ambil satu macrotask → kosongkan lagi semua microtask → ulangi.
Teka-teki klasiknya. Tulis dulu urutan tebakanmu sebelum menekan Jalankan.
Hasil
Tekan Jalankan untuk melihat hasilnya.
Ini jalan di browser kamu, di dalam sandbox. Apa pun yang kamu tulis di sini tidak bisa merusak situs.
Urutannya A, B, C, D, E. Kedua log sinkron jalan lebih dulu. Lalu microtask-nya, sesuai urutan masuknya ke queue. Timer-nya — sebuah macrotask — datang paling akhir, bahkan dengan nilai 0.
Sekarang dengan await ikut dicampur. Ingat: semua setelah await adalah microtask.
Hasil
Tekan Jalankan untuk melihat hasilnya.
Ini jalan di browser kamu, di dalam sandbox. Apa pun yang kamu tulis di sini tidak bisa merusak situs.
Microtask menyerobot antrean, berulang kali
Hasil
Tekan Jalankan untuk melihat hasilnya.
Ini jalan di browser kamu, di dalam sandbox. Apa pun yang kamu tulis di sini tidak bisa merusak situs.
// Do not run this. It freezes the tab as effectively as an infinite loop,
// while the call stack looks perfectly idle the entire time.
function starve() {
Promise.resolve().then(starve);
}
starve();
// The safe equivalent, which yields to the macrotask queue each turn:
function polite() {
setTimeout(polite, 0);
}Kenapa ini penting saat kerja
- Membaca value state persis setelah mengaturnya. Di React, pembaruannya masuk queue, belum diterapkan — ide penundaan yang sama.
- Test yang lolos di lokal tapi gagal di CI. Biasanya karena assertion-nya jalan sebelum microtask-nya selesai dikosongkan.
- `setTimeout(fn, 0)` sebagai "solusi". Kadang ia berhasil secara kebetulan, karena mendorong pekerjaan ke macrotask berikutnya. Memahami queue-nya berarti kamu bisa memilih dengan sengaja alih-alih menebak.
Tugas praktik
Tebak urutan output lengkapnya, tulis dulu, lalu jalankan di runner di atas. Kalau ketujuhnya tepat, berarti kamu paham loop-nya: console.log(1); setTimeout(() => console.log(2)); Promise.resolve().then(() => console.log(3)); (async () => { console.log(4); await null; console.log(5); })(); queueMicrotask(() => console.log(6)); console.log(7);
Hasil yang diharapkan
1, 4, 7, 3, 5, 6, 2
Cek pemahaman
Pilih jawaban dulu