ASYNC/AWAİT VE PROMİSE ZİNCİRLERİNİ DOĞRU YÖNETMEK
Asenkron kod, “çalışıyor” göründüğü halde üretimde sürpriz çıkarabilen bir alan. Bir yerde eksik await, başka yerde yutulan bir hata, bir başka akışta gereksiz seri çağrılar… Sonuç: belirsiz bug’lar, yavaşlayan sayfalar ve takip edilmesi zor iş akışları.
İyi haber şu: async/await ve Promise’ler doğru şekilde kurgulandığında hem okunabilirlik hem performans aynı anda iyileşir. Buradaki kritik nokta, zincir yönetimini ve hata yakalamayı rastgele değil, net kurallarla tasarlamaktır.
Bu yazıda; Promise zincirlerini sağlıklı kurmayı, paralel işleri kontrol etmeyi, hata yönetimini standartlaştırmayı ve gerçekçi örneklerle debug etmeyi ele alacağız. Daha fazla pratik için javascript eğitimi içeriğinde ileri seviye asenkron senaryoları uygulamalı çalışıyoruz.

Async/await ile Promise zincirini sadeleştirmek
Async/await, Promise zincirini daha okunabilir hale getirir; ancak “otomatik olarak doğru” hale getirmez. Yanlış yerde await kullanmak, gereksiz seri çalışmaya yol açabilir. Yanlış yerde try/catch kullanmak ise hatayı saklayabilir.
Ne zaman await, ne zaman return etmek
Bir fonksiyon Promise döndürüyorsa, bazı durumlarda sadece return etmek daha iyidir. Gereksiz await; stack’i uzatır, hata izini karmaşıklaştırabilir ve performansı etkileyebilir. Ama sonuç üzerinde işlem yapacaksanız await gerekir.
Okunabilirlik kazanırken kontrol kaybetmemek
“Her satıra await koymak” okur-yazar gibi görünse de, paralel yapılabilecek işleri seri hale getirir. Bunun yerine, işi başlatıp Promise’leri toplamak ve uygun yerde beklemek daha sağlıklıdır.
Hata yakalamayı tutarlı bir düzene oturtmak
Asenkron hataların en yaygın kaynağı, zincirin bir yerinde oluşan hatanın doğru yakalanmaması veya yanlış dönüştürülmesidir. Bir standardınız yoksa her ekip üyesi farklı bir yaklaşım uygular ve debug zorlaşır.
Try/catch sınırını doğru belirlemek
Try/catch’i her yere serpiştirmek yerine, anlamlı sınırlar çizmek gerekir: API çağrıları, veri dönüştürme, UI güncelleme gibi blokları ayırın. Böylece hangi katmanda hangi mesajı ürettiğiniz netleşir.
Yutulan hataları görünür kılmak
Catch içinde sadece console.log atıp devam etmek, üretimde sessiz bozulmalara neden olur. Hata gerçekten tolere edilecekse açıkça “fallback” üretin; tolere edilmeyecekse hatayı yukarı fırlatın. Bu yaklaşım, gizli başarısızlık riskini azaltır.
// Kötü: hatayı yutuyor, çağıran taraf "başarılı" sanıyor
async function getUserBad(id) {
try {
const res = await fetch("/api/users/" + id);
return await res.json();
} catch (e) {
console.log("Hata:", e);
return null; // sessiz bozulma
}
}
// İyi: hatayı anlamlı şekilde sarmalayıp yukarı iletmek
async function getUserGood(id) {
try {
const res = await fetch("/api/users/" + id);
if (!res.ok) throw new Error("HTTP " + res.status);
return await res.json();
} catch (e) {
throw new Error("getUser failed: " + e.message);
}
}Paralel işleri kontrol ederek performansı artırmak
Birçok iş birbirini beklemek zorunda değildir. Ürün verisi, fiyat bilgisi, stok bilgisi gibi kaynakları seri çekmek; toplam süreyi gereksiz uzatır. Doğru pattern; işi paralel başlatmak, sonucu birlikte beklemek ve birleşim noktasını netleştirmektir.
Promise.all ile paralel çalışmayı yönetmek
Bağımsız çağrıları Promise.all ile birlikte beklemek, toplam süreyi kısaltır. Ancak bir tanesi patladığında tüm sonuçlar boşa gider; bu nedenle “hepsi başarılı olmalı” senaryosunda kullanmak idealdir.
Promise.allSettled ile kısmi başarı senaryosu kurmak
Bazen “en azından bir kısmı gelsin” istenir. Örneğin yorumlar gelmezse sayfa yine açılsın, ama kullanıcıya uyarı gösterilsin. Bu durumda allSettled, başarısızları da kontrol etmenizi sağlar.
// Paralel başlat, birlikte bekle
async function loadPageData(productId) {
const [product, reviews] = await Promise.all([
fetch("/api/product/" + productId).then(r => r.json()),
fetch("/api/reviews?product=" + productId).then(r => r.json())
]);
return { product, reviews };
}
// Kısmi başarı: biri patlarsa bile sonucu işle
async function loadPageDataSoft(productId) {
const results = await Promise.allSettled([
fetch("/api/product/" + productId).then(r => r.json()),
fetch("/api/reviews?product=" + productId).then(r => r.json())
]);
const product = results[0].status === "fulfilled" ? results[0].value : null;
const reviews = results[1].status === "fulfilled" ? results[1].value : [];
const hasReviewError = results[1].status === "rejected";
return { product, reviews, hasReviewError };
}Zincir kopmalarını ve eksik await sorununu önlemek
“Niye catch’e düşmedi?” veya “Niye işlem tamamlanmadan UI güncellendi?” gibi soruların çoğu, zincirin bir yerinde promise’in dönmemesi veya await edilmemesinden çıkar. Bu tip hatalar, testte kaçıp üretimde yakalar.
Promise döndürmeyi unutmayı engellemek
then içinde yeni bir asenkron iş başlatıp return etmezseniz zincir kopar. Sonraki then, işi beklemeden çalışır. Bu hatayı görmenin yolu; her then bloğunda “burada promise dönüyor mu?” sorusunu sormaktır.
Fire-and-forget kullanımını kontrollü yapmak
Bazen “bekleme, arkada çalışsın” istenir (örneğin analytics gönderimi). Bu durumda bilinçli olarak await etmezsiniz ama hata yönetimini yine de planlarsınız: catch eklemek, timeout koymak veya retry stratejisi belirlemek gibi.
- Her asenkron çağrıyı ya await etmek ya da bilinçli şekilde arka plana bırakmak
- then içinde asenkron iş başlattıysanız return etmeyi unutmamak
- catch içinde fallback veya rethrow kararını netleştirmek
- Timeout ve retry gereken noktaları önceden belirlemek
İptal, timeout ve retry stratejisi tasarlamak
Gerçek hayatta istekler her zaman hızlı dönmez. Kullanıcı sayfadan ayrılır, arama metni değişir, ağ dalgalanır. Bu yüzden yalnızca “başarılı akış” değil; iptal, zaman aşımı ve tekrar deneme kurgusu da tasarlanmalıdır.
AbortController ile iptali devreye almak
Kullanıcı hızlıca arama yaparken önceki istekleri iptal etmek, hem maliyeti düşürür hem de yanlış verinin ekranda kalmasını engeller. İptali bir standart haline getirirseniz, “geç gelen yanıt kazandı” hatası azalır.
Zaman aşımı ve retry kararını netleştirmek
Her isteğe retry yapmak doğru değildir. Okuma istekleri retry’a uygun olabilir; ödeme gibi işlemlerde ise idempotency ve iş kuralları gerekir. Timeout ise kullanıcı deneyimi için kritiktir: çok uzun beklemek yerine kontrollü bir mesaj göstermek daha iyidir.

Debug sürecini hızlandırmak için iz sürmek
Asenkron hata ayıklamada hedef; “nerede patladı” değil, “hangi akış patlattı” sorusunu cevaplamaktır. Bunun için logları anlamlı hale getirmek ve akış kimliğiyle iz sürmek gerekir.
TraceId ile bir akışı baştan sona takip etmek
Tek bir kullanıcı etkileşimi, birden fazla asenkron işlemi tetikleyebilir. Hepsine aynı traceId’yi iliştirirseniz, loglar bir araya gelir ve zincirin kopma noktası daha hızlı bulunur.
Hata mesajını bağlamla zenginleştirmek
“Network error” tek başına bir şey söylemez. Endpoint, parametre, kullanıcı aksiyonu ve süre gibi bağlamları eklemek; hatayı yeniden üretmeyi kolaylaştırır. Bu da ekip içinde daha hızlı çözüm demektir.

Özet: Async/await ve Promise zincirlerini doğru yönetmek; bekleme noktalarını bilinçli seçmek, hatayı yutmamak, paralel işleri kontrol etmek ve iptal/timeout stratejisi kurmakla mümkün olur. Bu yaklaşım hem performansı artırır hem debug süresini kısaltır. Daha fazla senaryo ve pratik için javascript eğitimi içeriğine göz atabilirsiniz.


