WordPress, yazılım ve IT üzerine notlar
Anasayfa » Yazılım » JavaScript'te async/await: Callback cehenneminden okunabilir eşzamansız koda

JavaScript'te async/await: Callback cehenneminden okunabilir eşzamansız koda

İçindekiler
  1. 1. Callback'ler
  2. 2. Promise'ler
  3. 3. async/await
  4. fetch ile gerçek bir örnek
  5. Sık yapılan hatalar
  6. Sonuç

JavaScript tek iş parçacığında çalışır. Bir sunucudan veri beklerken, bir dosya okunurken ya da bir zamanlayıcı dolarken tarayıcının donmaması için bu işlemler eşzamansız (asynchronous) yapılır: İşlem başlatılır, JavaScript başka işlere devam eder ve işlem bittiğinde sonuçla ilgilenilir.

Bu modelin nasıl yazıldığı, yıllar içinde üç aşamadan geçti.

1. Callback'ler

kullaniciGetir(5, function (hata, kullanici) {
  if (hata) return goster(hata);
  siparisleriGetir(kullanici.id, function (hata, siparisler) {
    if (hata) return goster(hata);
    detayGetir(siparisler[0].id, function (hata, detay) {
      if (hata) return goster(hata);
      ekranaYaz(detay);
    });
  });
});

Her adım bir öncekinin içine yazılır ve kod sağa doğru kayarak "callback cehennemi" denen piramide dönüşür. Her seviyede hata kontrolünü elle yapmak gerekir.

2. Promise'ler

Promise, henüz tamamlanmamış bir işlemin gelecekteki sonucunu temsil eden bir nesnedir. then ile zincirlenir:

kullaniciGetir(5)
  .then(kullanici => siparisleriGetir(kullanici.id))
  .then(siparisler => detayGetir(siparisler[0].id))
  .then(detay => ekranaYaz(detay))
  .catch(hata => goster(hata));

Piramit düzleşti ve tek bir catch bütün zincirin hatalarını yakalıyor. Ama mantık karmaşıklaştıkça (koşullar, döngüler, ara değişkenler) then zincirleri de okunması zor hâle geliyor.

3. async/await

async function detayiGoster() {
  try {
    const kullanici = await kullaniciGetir(5);
    const siparisler = await siparisleriGetir(kullanici.id);
    const detay = await detayGetir(siparisler[0].id);
    ekranaYaz(detay);
  } catch (hata) {
    goster(hata);
  }
}

Kod yukarıdan aşağıya, senkron kod gibi okunuyor. Arka planda hâlâ Promise'ler kullanılıyor; async/await yalnızca onları yazmanın daha okunaklı bir yolu.

  • async ile işaretlenen bir fonksiyon her zaman bir Promise döndürür.
  • await, yalnızca async fonksiyonların içinde (ve modern ortamlarda modüllerin en üst seviyesinde) kullanılabilir. Promise tamamlanana kadar o fonksiyonun çalışmasını duraklatır, ama tarayıcıyı ya da sunucuyu dondurmaz.

fetch ile gerçek bir örnek

async function urunleriGetir() {
  const cevap = await fetch('/api/urunler?kategori=kitap');
  if (!cevap.ok) {
    throw new Error(`Sunucu hatası: ${cevap.status}`);
  }
  return cevap.json();
}

Burada çok yaygın bir tuzak var: fetch, sunucu 404 ya da 500 döndürdüğünde hata fırlatmaz. Yalnızca ağ bağlantısı tamamen başarısız olduğunda hata fırlatır. Bu yüzden cevap.ok kontrolünü kendiniz yapmalısınız.

Sık yapılan hatalar

1. Gereksiz sıralı bekleme

// Yavaş: İkinci istek, ilki bitene kadar başlamaz
const kullanici = await kullaniciGetir(5);
const kampanyalar = await kampanyalariGetir();

Bu iki istek birbirine bağlı değil. Paralel çalıştırın:

const [kullanici, kampanyalar] = await Promise.all([
  kullaniciGetir(5),
  kampanyalariGetir(),
]);

Her biri 300 milisaniye sürüyorsa, ilk sürüm 600, ikincisi yaklaşık 300 milisaniye sürer.

2. Promise.all ile Promise.allSettled farkı

Promise.all, isteklerden biri bile başarısız olursa hemen hata verir ve diğerlerinin sonuçlarını kaybedersiniz. Birbirinden bağımsız isteklerin hepsinin sonucunu görmek istiyorsanız Promise.allSettled kullanın:

const sonuclar = await Promise.allSettled([haberler(), hava(), doviz()]);
for (const s of sonuclar) {
  if (s.status === 'fulfilled') goster(s.value);
  else console.warn('Bir bileşen yüklenemedi:', s.reason);
}

Böylece döviz servisi çökse bile haberler ve hava durumu yine gösterilir.

3. Döngüde await

// Her ürün bir öncekini bekler
for (const id of urunIdleri) {
  await stokGuncelle(id);
}

Sıranın önemli olduğu durumlarda bu doğrudur. Önemli değilse paralel çalıştırın:

await Promise.all(urunIdleri.map(id => stokGuncelle(id)));

Ama bin ürünü aynı anda göndermek sunucuyu boğabilir. Çok sayıda istekte, istekleri 5'li ya da 10'lu gruplar hâlinde gönderin.

forEach ile await ise beklediğiniz gibi çalışmaz; forEach dönen Promise'leri beklemez:

// YANLIŞ: Döngü hemen biter, işlemler arkada başıboş devam eder
urunIdleri.forEach(async id => { await stokGuncelle(id); });

4. Yakalanmayan hatalar

async bir fonksiyonu await etmeden çağırırsanız ve içinde hata olursa, hata sessizce kaybolabilir ya da konsolda yalnızca "Unhandled promise rejection" uyarısı görünür. Her eşzamansız çağrının ya await edildiğinden ve bir try/catch içinde olduğundan ya da sonuna .catch() eklendiğinden emin olun.

5. Zaman aşımı yok

Cevap vermeyen bir sunucuya yapılan fetch isteği çok uzun süre bekleyebilir. Modern tarayıcılarda zaman aşımı eklemek kolay:

const cevap = await fetch('/api/rapor', { signal: AbortSignal.timeout(8000) });

Sonuç

async/await, eşzamansız kodu senkron kod kadar okunaklı yapar. Ama arka planda hâlâ Promise'lerin çalıştığını unutmayın. Bağımsız işleri paralel çalıştırın, fetch yanıtlarının durumunu kontrol edin ve hiçbir Promise'i başıboş bırakmayın.

Tezcan Kabakçı
Hosting teknik destek uzmanı ve WordPress uzmanı. 2024'ten beri bir hosting şirketinde teknik destek bölümünde çalışıyor; WordPress, DNS ve e-posta sorunlarını her gün yakından görüyor. YouTube kanalı.
Bu yazı 13 Temmuz 2026 tarihinde Yazılım kategorisinde yayınlandı.
Yeni yazılar e-postanıza gelsin

Ayda birkaç e-posta; istediğiniz an tek tıkla ayrılabilirsiniz.

Videolu anlatımlar YouTube'da
Sunucu ve WordPress sorunlarını kısa videolarla anlattığım kanala abone olabilirsiniz. ▶ Kanala abone ol

Yorumlar

Henüz yorum yok. İlk yorumu siz yazın.

Yorum yazın

Yorumlar onaylandıktan sonra yayımlanır. Bağlantı içeren yorumlar spam olarak değerlendirilebilir.