Event Loop’un Perde Arkası: Microtask vs Macrotask
JavaScript tek iş parçacıklı (single-threaded) bir dil olabilir, ama bu onun tek seferde sadece bir şey yapabildiği anlamına gelmez. Asıl sihir, Event Loop’ta gizlidir.
Event Loop'u, JavaScript motorunun kalp atışı gibi düşün. Kodlar sırayla değil, akıllıca organize edilmiş bir kuyruğa göre çalışır. Promise’ler, setTimeout’lar, hatta DOM güncellemeleri bile bu döngüde sıraya girer. Yani her “bekle!” dediğinde (örneğin bir fetch isteği ya da animasyon), bu kalp bir döngü daha atar.
Basit Bir Örnekle Başlayalım
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');
Çıktı sırasıyla “1, 2, 3, 4” gelir, dimi?Nope. Gerçek çıktı: 1, 4, 3, 2
Neden Böyle Oldu?
Çünkü JavaScript’te iki tür görev (task) kuyruğu vardır:
- Microtask Queue → Promise’ler,
queueMicrotaskgibi mini görevler buraya gider. - Macrotask Queue →
setTimeout,setInterval,requestAnimationFramegibi işler burada sıradadır.
- Call stack boş mu? Eğer evet → sıradaki microtask’leri çalıştır.
- Tüm microtask’ler bittikten sonra → ekranı render et.
- Ardından bir sonraki macrotask’e geç.
Promise (microtask) içindeki kod, setTimeout (macrotask) içindekinden önce çalışır.Event Loop Aşamaları Adım Adım
Event Loop’u kalp döngüsü gibi düşünelim:
- Call Stack (çağrı yığını): Senkron kodlar burada çalışır.
- Microtask Queue: Hızlı, küçük işler (Promise’ler, async/await sonuçları).
- Render Aşaması: Tarayıcı ekrana yeni görünümü çizer.
- Macrotask Queue: Daha uzun, planlı işler (setTimeout, network yanıtları, I/O).
Microtask Nedir?
Microtask’ler minik ama kritik görevlerdir. Genellikle Promise’lerin çözülmesi, DOM güncellemeleri sonrası mikro hesaplamalar ya da queueMicrotask() ile planlanan küçük işler bu kategoriye girer.
queueMicrotask(() => {
console.log('Bu, event loop bitiminde çalışır.');
});
Bir loop turu biter bitmez, render’dan hemen önce çalışırlar. Yani küçük ama hızlı görevler için mükemmeldirler.
Ama dikkat! Sonsuz microtask zincirleri (Promise.then().then().then()...) oluşturursan render işlemi gecikir, çünkü tarayıcı çizim yapmadan sürekli microtask çalıştırmakla meşgul olur. 🐢
Macrotask Nedir?
Macrotask’ler, bir sonraki döngüde yapılacak işlerdir. Tarayıcı animasyonları, kullanıcı etkileşimleri, ağ yanıtları hep buraya girer.
setTimeout(() => console.log('Macrotask!'), 0);
0 milisaniye bile versen, bu kod bir sonraki event loop turunda çalışır. Çünkü önce microtask kuyruğunun boşalması beklenir. Yani 0 demek “şimdi hemen” değil, “birazdan, sıran gelince” demektir. 😊
Karşılaştırma Tablosu
| Özellik | Microtask | Macrotask |
|---|---|---|
| Örnekler | Promise, queueMicrotask | setTimeout, setInterval, requestAnimationFrame |
| Zamanlama | Aynı event loop turunda, render öncesi | Sonraki event loop turunda |
| Kullanım Alanı | Küçük ve acil işlemler | Daha büyük veya gecikmeli işler |
| Performans Etkisi | Düşük | Orta (render gecikebilir) |
| Sıradaki Öncelik | Daha yüksek | Daha düşük |
Node.js Dünyasında
Node tarafında işler biraz farklı görünür ama mantık aynıdır. process.nextTick() microtask gibi, setImmediate() ise macrotask gibidir.
setTimeout(() => console.log('timeout'), 0);
setImmediate(() => console.log('immediate'));
process.nextTick(() => console.log('nextTick'));
Çıktı genellikle: nextTick → timeout → immediate olur. Node.js bile aynı prensibi takip eder, sadece kendi kuyruğu biraz farklı isimlendirilmiştir.
Gerçek Hayattan Bir Analoji
Event Loop’u bir konser sahnesi gibi düşün.
- Microtask’ler: Sahne arkasında ışık ve ses ayarını yapan ekip -- her performans sonrası hızla girip küçük düzeltmeler yaparlar.
- Macrotask’ler: Bir sonraki sanatçı -- sahneye çıkmadan önce sırada bekler.
- Render: Seyircinin gerçekten gördüğü sahne.
İşte bu yüzden Promise’ler (microtask) her zaman
setTimeout’lardan (macrotask) bir adım önde çalar.Performans ve Tuzaklar
- Çok fazla microtask, render gecikmesine neden olabilir. Tarayıcı hiçbir zaman “şimdi ekrana çizim yapayım” diyemez.
- Aşırı
setTimeoutkullanımı ise CPU’yu yorar ve frame drop yaşanır. - Promise zincirleri oluştururken araya küçük gecikmeler (
setTimeout) eklemek bazen performansı dengeleyebilir.
Sonuç
Event Loop JavaScript’in görünmeyen kahramanıdır. Onu anlamak, async davranışları tahmin edebilmeni sağlar. Promise ve setTimeout arasındaki fark küçük görünse de, UI hissinde dev bir fark yaratır.
Microtask’leri hızlı işlemler için, macrotask’leri zaman planlaması gerektiren işler için kullan.