# Task Queue vs Microtask Queue: Memahami Urutan Eksekusi Javascript

Pernah merasa bingung mengapa `setTimeout` tidak langsung dieksekusi meskipun sudah dipanggil dengan timeout=0? Atau mungkin kamu sering menggunakan Promise tetapi belum sepenuhnya memahami bagaimana JavaScript menangani operasi seperti ini?

Di JavaScript, ada dua mekanisme penting yang mempengaruhi urutan eksekusi operasi asynchronous: **task queue** dan **microtask queue**.

Mari kita telusuri lebih dalam bagaimana masing-masing mekanisme ini berfungsi dan bagaimana mereka memengaruhi urutan eksekusi.

### Task Queue: Antrian Utama Event Loop

**Task Queue** adalah antrian utama yang digunakan oleh Web API untuk mengirimkan callback. Ketika kamu menjalankan `setTimeout` atau mengirimkan AJAX request, JavaScript akan menaruh callback-nya ke dalam task queue.

**Contoh task yang masuk ke task queue:**

* Callback dari `setTimeout`
    
* Callback dari `setInterval`
    
* Request AJAX
    

Nah, yang penting diingat: **event loop akan memproses task queue hanya setelah semua task di call stack selesai.** Tapi sebelum itu, event loop akan memprioritaskan yang ada di microtask queue dulu. Lho, apa itu microtask queue?

### Microtask Queue: Antrian Yang Di Prioritaskan

**Microtask Queue** menampung task-task dengan ukuran lebih kecil yang diprioritaskan untuk dieksekusi sebelum task di task queue (juga dikenal sebagai macro task). Ini berarti microtask selalu didahulukan jika call stack sudah kosong, bahkan sebelum task lain dijalankan.

**Karakteristik utama dari microtask queue:**

1. **Semua task di microtask queue akan dieksekusi terlebih dahulu sebelum task di task queue.**
    
2. Task ini biasanya melibatkan operasi yang harus segera diselesaikan setelah eksekusi kode saat ini.
    

**Contoh microtask:**

* Promise (`.then` atau `.catch`)
    
* API `fetch`
    
* `MutationObserver`
    
* Kamu juga bisa menambahkan microtask secara manual menggunakan `queueMicrotask()`
    

Berikut adalah ilustrasi yang sangat bagus dari sebuah artikel yang menjelaskan [microtask dan macrotask dari situs dev.to](https://dev.to/lydiahallie/javascript-visualized-event-loop-3dif) oleh Lydia Hallie.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1728057409467/7bef89c7-4111-49c3-9235-4f7d97501ff0.gif align="center")

Disini kita bisa lihat secara langsung bahwa event loop akan memproses semua task yang ada pada microtask sampai selesai. Setelah itu event loop akan lanjut memproses task tang ada pada task queue (macrotask).

### Kapan Harus Menggunakan Microtask?

Karena task di microtask queue diprioritaskan, kamu mungkin berpikir microtask selalu lebih baik. Tapi tidak juga! Ada beberapa situasi tertentu di mana microtask sebaiknya digunakan, dan ada juga ketika macro task (seperti `setTimeout`) lebih tepat.

#### 1\. **Prioritas Eksekusi**

Microtask ideal ketika kamu perlu menjalankan task setelah kode di call stack selesai, tetapi sebelum browser melakukan rendering atau memproses event dari user. Beberapa kasus umum:

* **Operasi yang bergantung pada hasil kode sebelumnya**. Misalnya, jika kamu punya sebuah promise yang harus menyelesaikan eksekusi sebelum user bisa melakukan interaksi lagi.
    
* **Task yang tidak boleh memblokir UI atau penanganan event.** Dengan microtask, kamu bisa memastikan UI tetap responsif karena eksekusi task dilakukan di sela-sela event user dan rendering UI.
    

#### 2\. **Batching Process**

Microtask sangat berguna untuk mengelompokkan beberapa operasi asinkron sehingga lebih efisien. Alih-alih menjalankan task satu per satu, kamu bisa mengelompokkannya dalam satu batch sehingga:

* Operasi-operasi dieksekusi bersamaan.
    
* Mengurangi beban pada event loop dan menghindari penundaan yang terlalu lama.
    
* Membuat aplikasi lebih cepat dan lebih responsif.
    

### Contoh Praktis: Mengambil Data dari API dan Memperbarui UI

Mari kita lihat sebuah contoh sederhana. Misalkan kamu mengambil data dari API dan ingin memperbarui UI setelah datanya diterima.

```bash
javascriptCopy codefetch('/api/data')
  .then(response => response.json())
  .then(data => updateUI(data));
```

Di contoh ini, kita menggunakan Promise untuk menunggu respons dari server:

* Promise secara otomatis menggunakan microtask queue untuk menjalankan callback.
    
* Pemrosesan data dan pembaruan UI terjadi segera setelah data diterima, sebelum browser melakukan rendering.
    
* Ini memastikan pembaruan UI terjadi dengan cepat dan mulus.
    

### Dampak Penggunaan Microtask terhadap Performa

Meski microtask memiliki prioritas tinggi, **penggunaan yang tidak tepat dapat mengganggu performa aplikasi.** Terlalu banyak microtask yang menumpuk bisa membuat aplikasi terasa lambat karena mereka akan memblokir task di task queue yang juga penting, seperti pembaruan UI atau penanganan event user.

Berikut beberapa tips agar penggunaan microtask tidak merugikan performa:

1. **Jangan overuse microtask**. Gunakan hanya ketika kamu benar-benar butuh operasi berprioritas tinggi yang harus segera dijalankan.
    
2. **Batching task dengan bijak.** Jika kamu memiliki beberapa task asinkron, kelola mereka dalam satu batch menggunakan microtask, tetapi jangan membebani event loop terlalu banyak sekaligus.
    
3. **Pantau dampak pada UI.** Selalu pastikan bahwa penggunaan microtask tidak menyebabkan masalah seperti stuttering pada UI atau membuat interaksi user terasa lambat.
    

### Contoh Kasus: Pertanyaan Interview Yang Sering Ditanyakan

Coba tebak apa output dari program berikut? sebelum lanjut scroll kebawah coba tuliskan dulu jawaban mu di sebuah kertas dan nanti kita akan cocok kan dengan penjelasn yang ada dibawah kode ini.

```javascript
console.log('Start');

setTimeout(() => {
  console.log('Timeout 1');
}, 0);

Promise.resolve().then(() => {
  console.log('Promise 1');
});

new Promise((resolve, reject) => {
  console.log('New Promise 1');
  resolve();
}).then(() => {
  console.log('Promise 2');
});

console.log('End');
```

### 1\. `console.log('Start')`

Ini adalah *synchronous task*, sehingga langsung dieksekusi begitu kode dijalankan. Maka, output pertama yang akan muncul di console adalah:

```bash
Start
```

### 2\. `setTimeout(() => { console.log('Timeout 1'); }, 0)`

`setTimeout` adalah asynchronous task, dan callback-nya akan dimasukkan ke **task queue** (macro task), tapi **hanya** setelah semua synchronous task selesai dieksekusi. Karena waktu delay yang diberikan adalah `0`, callback akan dijalankan sesegera mungkin, tetapi hanya setelah semua microtask selesai.

Untuk saat ini, callback ini akan **menunggu di task queue** sampai event loop siap memproses task queue setelah semua microtask selesai. Jadi, fungsi callback dari `setTimeout` ini **belum bisa diekseskusi** dan akan menunggu call stack menyelesaikan synchronous task dan task yang ada pada antrian microtask.

### 3\. `Promise.resolve().then(() => { console.log('Promise 1'); });`

`Promise.resolve()` segera memanggil callback `then()`, yang akan dijadwalkan sebagai **microtask**. Microtask diprioritaskan lebih tinggi daripada macro task (seperti `setTimeout` diatas), jadi callback ini akan diproses **setelah synchronous code selesai**, namun **sebelum** callback `setTimeout` dari task queue.

Saat ini, microtask ini **menunggu dalam microtask queue** sampai synchronous task selesai.

### 4\. `new Promise((resolve, reject) => { console.log('New Promise 1'); resolve(); }).then(() => { console.log('Promise 2'); });`

* Kode dalam constructor `new Promise` dieksekusi secara langsung (sync) saat Promise dibuat. Jadi, perintah synchronous `console.log('New Promise 1')` akan langsung dieksekusi:
    
    ```bash
    New Promise 1
    ```
    
* Setelah itu, karena `resolve()` dipanggil, callback `then()` akan ditambahkan ke **microtask queue**. Sama seperti `Promise.resolve()`, callback ini akan dieksekusi **setelah semua synchronous task selesai**.
    

Jadi, sekarang ada dua callback microtask dalam antrian (dari dua promise), menunggu untuk dieksekusi setelah synchronous code selesai.

### 5\. `console.log('End')`

Ini adalah perintah synchronous lainnya, jadi langsung dieksekusi setelah semua kode di atas. Output di console selanjutnya adalah:

```bash
End
```

Sekarang, setelah semua synchronous code selesai dan call stack kosong, event loop akan memproses **microtask queue**.

Jadi output dari kedua `Promise.then()` callback adalah:

```bash
Promise 1
Promise 2
```

Terakhir, event loop akan memproses callback di task queue, yakni `console.log('Timeout 1')`.

Yang menarik disini adalah jika anda mengatur waktu timeout=0, itu maksudnya adalah **callback akan dikirimkan ke task queue** setelah 0ms. **Bukan callback akan di eksekusi** setelah 0ms.

Jadi meskipun timeout-nya 0ms, eksekusi callback bisa tertunda, misalnya hingga 100ms, tergantung pada panjangnya antrian task yang harus diproses lebih dulu.

Semoga jelas ya perbedaannya.

```bash
Timeout 1
```

Jadi, output akhir di console adalah:

```bash
Start
New Promise 1
End
Promise 1
Promise 2
Timeout 1
```

Dengan mengerti urutan eksekusi synchronous tasks, **microtasks** (seperti `Promise`), dan **macro tasks** (seperti `setTimeout`), kita akan dapat mengoptimalkan performa aplikasi dan memastikan kode berjalan sesuai harapan.
