Lompat ke konten
Kembali ke modul

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.

MicrotaskMacrotask
Dibuat oleh.then, await, queueMicrotasksetTimeout, setInterval, I/O, event
PrioritasLebih tinggiLebih rendah
DikosongkanSemuanya, sampai habisSatu, 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.

Coba sendiri

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.

Coba sendiri

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

Coba sendiri

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.

js
// 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

console.log(1); setTimeout(() => console.log(2)); Promise.resolve().then(() => console.log(3)); console.log(4); — urutan cetaknya apa?

Pilih jawaban dulu