JAVASCRİPT EVENT LOOP MANTIĞINI ANLAMAK VE HATA AYIKLAMAK
Bazı hatalar vardır: “Bazen oluyor, bazen olmuyor.” Konsolda doğru görünen log’lar, arada bir ters sırada çalışan callback’ler, UI’nin donup sonra toparlanması… Bu tip sorunların çoğu, event loop’un nasıl çalıştığını tam bilmemekten doğar.
JavaScript tek iş parçacığıyla çalışır ama asenkron işleri de aynı anda yürütüyormuş gibi hissettirir. Bu sihir; call stack, task queue ve microtask queue arasındaki düzenle sağlanır. Düzeni bildiğinizde, “neden önce bu çalıştı?” sorusu net bir cevaba kavuşur.
Bu yazıda event loop mantığını sezgisel bir modelle ele alacağız; ardından gerçekçi örneklerle promise, setTimeout ve DOM event’lerinin sırasını görerek hata ayıklama yaklaşımını güçlendireceğiz. Derinleşmek isterseniz javascript eğitimi içeriğinde asenkron programlama ve debugging pratiklerini daha kapsamlı uyguluyoruz.

Event loop bileşenlerini zihinde modellemek
Event loop’u anlamanın en hızlı yolu, onu bir trafik polisi gibi düşünmektir: sırayla gelen işleri alır, doğru kuyruğa yerleştirir ve uygun zamanda çalıştırır. Buradaki kritik nokta; her işin aynı kuyruğa gitmemesidir.
Call stack üzerinden senkron akışı okumak
Senkron kod, call stack üzerinde çalışır ve bitmeden diğer işlere geçilmez. Bu yüzden uzun süren bir döngü veya bloklayan bir işlem, kullanıcı etkileşimini geciktirebilir. Debugging’de ilk kontrol, “stack’i ne dolduruyor?” sorusuyla başlamalıdır.
Task queue ve microtask queue farkını görmek
Timer’lar (setTimeout), bazı tarayıcı API’leri ve mesajlaşma mekanizmaları genellikle task queue’ya düşer. Promise zincirleri ve queueMicrotask ise microtask queue’ya düşer. Event loop, her “tur” sonunda microtask’leri bitirmeden yeni task’a geçmez; bu detay sıralama hatalarının ana kaynağıdır.
Çalışma sırasını belirleyen kuralları kavramak
“Önce hangisi çalışır?” sorusunu tahminle değil kuralla çözmek gerekir. Bu kurallar, pratikte log sırasını açıklamanın yanı sıra performans sorunlarını da görünür kılar.
Microtask önceliğini pratikte kanıtlamak
Promise callback’leri, mevcut senkron iş bittikten hemen sonra devreye girer; ardından sıradaki task’lar çalışır. Bu yüzden setTimeout(0) “hemen” çalışmaz; çoğu zaman promise then’lerden sonra çalışır. Bu farkı bilmek, “race condition” benzeri hataları hızlı açıklamayı sağlar.
Render ve kullanıcı etkileşimi ile ilişkisini anlamak
Tarayıcı, render için de zaman ister. Microtask’leri arka arkaya zincirlemek, tarayıcının render fırsatını geciktirebilir. Bu da “UI dondu” hissi yaratır. Hata ayıklarken yalnızca fonksiyon sırası değil, render aralıkları da düşünülmelidir.
// Beklenen çıktı sırasını tahmin edin:
console.log("A");
setTimeout(() => {
console.log("B: timeout");
}, 0);
Promise.resolve()
.then(() => console.log("C: promise then 1"))
.then(() => console.log("D: promise then 2"));
console.log("E");
// Tipik çıktı:
// A
// E
// C: promise then 1
// D: promise then 2
// B: timeoutAsenkron hataları yakalamak için debug yaklaşımı kurmak
Event loop kaynaklı hatalar çoğu zaman “yanlış sırada güncelleme” veya “beklenmeyen zamanda state değişimi” olarak görünür. Bu hataları çözmek için sistematik bir debug rutini gerekir.
Log yerine zaman damgalı iz sürmek
Basit console.log çoğu zaman yetmez; çünkü “ne zaman” sorusu kaybolur. Zaman damgası, çağrı kimliği ve işlem adımı eklemek; aynı anda çalışan akışları ayırmayı kolaylaştırır. Bu yaklaşım, özellikle API istekleri ve UI etkileşimi birleştiğinde işe yarar.
Breakpoint ve async stack ile kaynağa inmek
Tarayıcı devtools, async stack izini takip etmeyi destekler. Böylece callback’e nasıl gelindiğini, hangi promise zincirinin tetiklediğini görebilirsiniz. Hata ayıklarken hedef; semptomu değil, işi kuyruğa atan noktayı bulmaktır.
- Akışı etiketlemek için requestId veya traceId üretmek
- Zamanlama için performance.now() ile ölçmek
- Kuyruk türünü ayırt etmek (promise mi timer mı)
- İptal senaryosu eklemek (abort, flag, cleanup)
Gerçek hayatta görülen senaryoları çözümlemek
Teori, pratikte iki senaryoda karşımıza çıkar: UI güncellemesi ve network sonuçları. İkisi de “önce gelen kazanır” gibi basit bir mantıkla çözülmez; event loop sırası ve state yönetimi belirleyicidir.
Race condition benzeri sorunları azaltmak
Kullanıcı hızlıca filtre değiştirir, iki istek art arda gider ve geç gelen yanıt ekranda kalır… Bu tip durumda sorun event loop değil, event loop üzerinde üst üste binen işlerin yönetilmemesidir. Çözüm; “son istek kazanır” kuralı koymak veya istekleri iptal edebilmektir.
Timer ile promise karışımında sıra hatasını tespit etmek
Bazı kodlar hem setTimeout hem promise kullanır; bu da “bazı cihazlarda farklı sırada” gibi algılanan davranışlara neden olabilir. Çözüm; kritik sırayı tek bir mekanizmaya indirgemek veya açık bir durum makinesiyle ilerlemektir.
// Basit bir "son istek kazanır" yaklaşımı:
let latestRequest = 0;
async function fetchProducts(query) {
const requestId = ++latestRequest;
const res = await fetch("/api/products?q=" + encodeURIComponent(query));
const data = await res.json();
// Geç gelen istekleri yok saymak:
if (requestId !== latestRequest) return;
renderProducts(data);
}
// UI event:
document.querySelector("#search").addEventListener("input", (e) => {
fetchProducts(e.target.value);
});Performansı bozan event loop anti-patternlerini fark etmek
Asenkron kod hatası yalnızca sıra ile ilgili değildir; performans da işin içindedir. Özellikle microtask’leri aşırı zincirlemek, render fırsatlarını kısıtlayabilir ve kullanıcı deneyimini zayıflatabilir.
Uzun süren senkron işleri küçük parçalara bölmek
Büyük veri işleme veya yoğun DOM işlemleri, tek parçada yapıldığında call stack’i uzun süre meşgul eder. İşleri parçalara bölmek, kullanıcı etkileşimi ve render için nefes alanı bırakır. Bu yaklaşım, “tıklandı ama tepki vermedi” hissini azaltır.
Microtask selini kontrol altına almak
Promise zincirleri ile art arda mikro işler üretmek, tarayıcının her turda microtask’leri bitirme kuralı nedeniyle render gecikmesine yol açabilir. Kritik olmayan işleri task tarafına taşımak veya araya planlı boşluklar eklemek daha dengeli olur.
Ekiplere uygun bir hata ayıklama standardı oluşturmak
Event loop bilgisi bireysel bir kazanım gibi görünse de, ekip içinde ortak bir dil olduğunda çok daha hızlı sonuç verir. PR yorumlarında “microtask önceliği” veya “stack’i bloklama” gibi net ifadeler, tartışmayı kısaltır.
Paylaşılan kontrol listesiyle üretime daha güvenli çıkmak
Asenkron değişikliklerde basit bir kontrol listesi, regressions riskini düşürür. Özellikle UI güncellemesi, API istekleri ve state yönetimi birleştiğinde kontrol listesi hayat kurtarır.
- Sıra bağımlılığı var mı, açıkça yazmak
- İptal/cleanup senaryosunu eklemek
- Hata yakalama ve fallback akışını planlamak
- Performans etkisi için ölçüm eklemek
Özet: JavaScript event loop mantığını bildiğinizde, “rastgele” görünen bug’ların çoğu açıklanabilir hale gelir. Senkron akışı stack üzerinden okumak, microtask ve task kuyruğunu ayırt etmek, render etkisini düşünmek ve iz sürmeyi standartlaştırmak; hata ayıklamayı hızlandırır. Bu pratiği daha fazla örnekle pekiştirmek için javascript eğitimi içeriğinde asenkron akış ve debugging senaryolarını uygulamalı ele alıyoruz.



