---
title: 'Hata ve Yeniden Deneme Kalıpları'
source: 'https://academia.sh/tr/kurslar/eszamansiz-javascript/hata-ve-yeniden-deneme-kaliplari'
course: 'Eşzamansız JavaScript ve Çalışma Zamanı'
language: tr
updated: '2026-08-17T18:09:44+00:00'
license: 'CC BY-SA 4.0'
---

# Hata ve Yeniden Deneme Kalıpları

Geçici ve kalıcı hataların ayrımı, artan bekleme süreleriyle yeniden deneme, deneme bütçesi, işlem anahtarıyla yinelemeye dayanıklılık ve toplu başarısızlığın önlenmesi.

İptal ve zaman aşımı, başarısızlığı belirlenmiş hale getirdi: bir isteğin ne zaman
biteceği artık biliniyor. Geriye başarısızlıktan sonra ne yapılacağı kalıyor.

Her başarısızlık aynı değildir. Bazıları kendiliğinden geçer — kısa süreli aşırı yük, tek
bir paketin kaybı. Bazıları tekrarlandığında aynı sonucu verir — yanlış adres, bilinmeyen
istasyon. Bu ders ayrımı kurar, yeniden denemenin doğru biçimini yazar ve hangi işlemlerin
güvenle tekrarlanabileceğini belirler.

## Geçici ve Kalıcı Hata

Yeniden denemenin tek gerekçesi vardır: **aynı isteğin bu kez başarılı olma olasılığı.**
Bu olasılık yoksa deneme, hem çağıranı bekletir hem sunucuyu meşgul eder.

| Hata | Sınıf | Gerekçe |
|---|---|---|
| Bağlantı kurulamadı | Geçici | Ağ koşulu değişebilir |
| 408, 429 | Geçici | Süre veya hız sınırı; sonra denenebilir |
| 500, 502, 503, 504 | Geçici | Sunucu tarafında geçici durum |
| 400, 401, 403, 404 | Kalıcı | İstek yeniden gönderilse de aynı sonuç |

Ayrım biçimsel değil anlamsaldır: 404, "bu kaynak yok" der; yeniden sormak yanıtı
değiştirmez. 503 ise "şu an hizmet veremiyorum" der; bu, zamana bağlı bir ifadedir.

Ağ hatası ile başarısız durum kodu arasındaki ayrım da burada belirleyici olur. Önceki
derste gösterildiği gibi, ağ hatası isteğin sunucuya ulaşıp ulaşmadığını bilmediğimiz
durumdur. Bu belirsizlik, yan etkili işlemlerde yeniden denemeyi tehlikeli kılar.

## Artan Bekleme ile Yeniden Deneme

Başarısız bir isteği hemen tekrarlamak, sorunu ağırlaştırır: aşırı yüklü bir sunucu, aynı
anda vazgeçen bütün istemcilerin tekrar denemesiyle karşılaşır. Bu nedenle bekleme süresi
her denemede artırılır — **üstel geri çekilme (exponential backoff)**.

```js
import { createServer } from "node:http";

let istekSayisi = 0;

const sunucu = createServer((istek, yanit) => {
  istekSayisi += 1;
  if (istekSayisi <= 2) {
    yanit.writeHead(503, { "content-type": "application/json" });
    yanit.end(JSON.stringify({ hata: "hizmet geçici olarak kullanılamıyor" }));
    return;
  }
  yanit.writeHead(200, { "content-type": "application/json" });
  yanit.end(JSON.stringify({ istasyon: "A1", deger: 21.4 }));
});

await new Promise((karar) => sunucu.listen(0, "127.0.0.1", karar));
const taban = `http://127.0.0.1:${sunucu.address().port}`;

const GECICI_DURUMLAR = new Set([408, 429, 500, 502, 503, 504]);

function bekle(sure) {
  return new Promise((karar) => setTimeout(karar, sure));
}

async function denemeliGetir(adres, denemeSayisi, tabanBekleme) {
  let sonHata = null;

  for (let deneme = 1; deneme <= denemeSayisi; deneme += 1) {
    const yanit = await fetch(adres);
    if (yanit.ok) {
      console.log("deneme", deneme, "— başarılı");
      return await yanit.json();
    }
    if (!GECICI_DURUMLAR.has(yanit.status)) {
      throw new Error(`kalıcı hata: ${yanit.status}`);
    }
    sonHata = new Error(`geçici hata: ${yanit.status}`);
    console.log("deneme", deneme, "— geçici hata:", yanit.status);

    if (deneme < denemeSayisi) {
      const bekleme = tabanBekleme * 2 ** (deneme - 1);
      console.log("  planlanan bekleme:", bekleme, "ms");
      await bekle(bekleme);
    }
  }

  throw new Error(`deneme bütçesi tükendi: ${denemeSayisi}`, { cause: sonHata });
}

console.log("sonuç:", await denemeliGetir(`${taban}/olcum/A1`, 4, 10));

sunucu.close();
```

```
deneme 1 — geçici hata: 503
  planlanan bekleme: 10 ms
deneme 2 — geçici hata: 503
  planlanan bekleme: 20 ms
deneme 3 — başarılı
sonuç: { istasyon: 'A1', deger: 21.4 }
```

Sunucu ilk iki isteği geri çevirecek biçimde kuruldu; üçüncü deneme başarılı oldu.
Yazdırılan bekleme değerleri ölçüm değil, hesaplanmış plandır: taban değer her denemede
ikiyle çarpılır.

Gerçek dağıtımlarda bu plana bir de rastgele sapma eklenir. Gerekçesi şudur: aynı anda
başarısız olan istemciler aynı planı uygularsa, yeniden denemeleri de aynı ana denk gelir
ve sunucu ikinci bir dalgayla karşılaşır. Sapma, dalgayı zamana yayar. Bu derste
çıktıların belirlenimci kalması için sapma kullanılmadı.

## Deneme Bütçesi ve Kalıcı Hatada Erken Çıkış

İki koruma zorunludur. Deneme sayısı sınırlanmalıdır, yoksa hizmet dönmediğinde çağıran
sonsuza kadar bekler. Kalıcı hatada ise hiç beklenmemelidir.

```js
import { createServer } from "node:http";

const sunucu = createServer((istek, yanit) => {
  const yol = new URL(istek.url, "http://yerel").pathname;
  if (yol === "/olcum/Z9") {
    yanit.writeHead(404, { "content-type": "application/json" });
    yanit.end(JSON.stringify({ hata: "bilinmeyen istasyon" }));
    return;
  }
  yanit.writeHead(503, { "content-type": "application/json" });
  yanit.end(JSON.stringify({ hata: "hizmet geçici olarak kullanılamıyor" }));
});

await new Promise((karar) => sunucu.listen(0, "127.0.0.1", karar));
const taban = `http://127.0.0.1:${sunucu.address().port}`;

const GECICI_DURUMLAR = new Set([408, 429, 500, 502, 503, 504]);

function bekle(sure) {
  return new Promise((karar) => setTimeout(karar, sure));
}

async function denemeliGetir(adres, denemeSayisi, tabanBekleme) {
  let sonHata = null;

  for (let deneme = 1; deneme <= denemeSayisi; deneme += 1) {
    const yanit = await fetch(adres);
    if (yanit.ok) return await yanit.json();
    if (!GECICI_DURUMLAR.has(yanit.status)) {
      throw new Error(`kalıcı hata: ${yanit.status}`);
    }
    sonHata = new Error(`geçici hata: ${yanit.status}`);
    console.log("deneme", deneme, "— geçici hata:", yanit.status);
    if (deneme < denemeSayisi) await bekle(tabanBekleme * 2 ** (deneme - 1));
  }

  throw new Error(`deneme bütçesi tükendi: ${denemeSayisi}`, { cause: sonHata });
}

try {
  await denemeliGetir(`${taban}/olcum/Z9`, 4, 10);
} catch (hata) {
  console.log("kalıcı hatada hiç beklenmedi:", hata.message);
}

try {
  await denemeliGetir(`${taban}/olcum/A1`, 3, 10);
} catch (hata) {
  console.log("son hata:", hata.message);
  console.log("kök neden:", hata.cause.message);
}

sunucu.close();
```

```
kalıcı hatada hiç beklenmedi: kalıcı hata: 404
deneme 1 — geçici hata: 503
deneme 2 — geçici hata: 503
deneme 3 — geçici hata: 503
son hata: deneme bütçesi tükendi: 3
kök neden: geçici hata: 503
```

Bilinmeyen istasyon için tek istek yapıldı; geçici hata veren adres için üç deneme
tüketildi ve bütçe dolduğunda hata, kök nedeni taşıyarak yukarı verildi. Hata yönetimi
dersindeki `cause` kalıbı burada tanı için gereklidir: çağıran hem "kaç denemede
vazgeçildiğini" hem "en son ne olduğunu" görür.

Yeniden denemenin toplam süresi de bir sınırdır. Deneme sayısı ile bekleme planı
birleşince, en kötü durumda geçecek süre baştan hesaplanabilir olmalıdır; iptal işareti bu
toplam süreye de bağlanabilir.

## Yan Etkili İşlemleri Yeniden Denemek

Ölçüm okumak zararsızdır: aynı isteği iki kez göndermek yalnız fazladan iş yapar. Ölçüm
**yazmak** öyle değildir. Ağ hatası aldığınızda isteğin sunucuya ulaşıp ulaşmadığını
bilemezsiniz; tekrar gönderirseniz aynı kayıt iki kez oluşabilir.

Çözüm, isteğe onu tekil kılan bir **işlem anahtarı** eklemektir. Sunucu, aynı anahtarla
gelen ikinci isteği yeni bir işlem saymaz; ilk işlemin sonucunu döndürür.

```js
import { createServer } from "node:http";

const kayitlar = [];

const sunucu = createServer((istek, yanit) => {
  let govde = "";
  istek.on("data", (parca) => {
    govde += parca;
  });
  istek.on("end", () => {
    const anahtar = istek.headers["x-islem-anahtari"];
    const varOlan = anahtar === undefined ? undefined : kayitlar.find((k) => k.anahtar === anahtar);

    if (varOlan === undefined) {
      kayitlar.push({ anahtar, veri: JSON.parse(govde) });
    }
    yanit.writeHead(200, { "content-type": "application/json" });
    yanit.end(JSON.stringify({ toplamKayit: kayitlar.length }));
  });
});

await new Promise((karar) => sunucu.listen(0, "127.0.0.1", karar));
const taban = `http://127.0.0.1:${sunucu.address().port}`;

async function olcumYaz(basliklar) {
  const yanit = await fetch(`${taban}/kayit`, {
    method: "POST",
    headers: { "content-type": "application/json", ...basliklar },
    body: JSON.stringify({ istasyon: "A1", deger: 21.4 }),
  });
  return await yanit.json();
}

console.log("anahtarsız 1. deneme:", await olcumYaz({}));
console.log("anahtarsız 2. deneme:", await olcumYaz({}));
console.log("anahtarlı 1. deneme:", await olcumYaz({ "x-islem-anahtari": "yazma-77" }));
console.log("anahtarlı 2. deneme:", await olcumYaz({ "x-islem-anahtari": "yazma-77" }));

sunucu.close();
```

```
anahtarsız 1. deneme: { toplamKayit: 1 }
anahtarsız 2. deneme: { toplamKayit: 2 }
anahtarlı 1. deneme: { toplamKayit: 3 }
anahtarlı 2. deneme: { toplamKayit: 3 }
```

Anahtarsız iki istek iki kayıt üretti; anahtarlı iki istek tek kayıt üretti. Aynı isteğin
tekrarının sonucu değiştirmemesi özelliğine **yinelemeye dayanıklılık (idempotence)**
denir; İnternet Nasıl Çalışır kursunda aynı özellik HTTP yöntemleri için tanıtılmıştı.
Buradaki eklenti, özelliğin yöntemden gelmediği durumda anahtarla sağlanabilmesidir.

Kural şu biçimde özetlenir: **yeniden deneme, ancak işlem yinelemeye dayanıklıysa
güvenlidir.** Değilse önce dayanıklı hale getirilmeli, sonra denenmelidir.

## Yeniden Denemenin Sınırı

Yeniden deneme, tek bir isteği kurtarır; sistemi kurtarmaz. Hizmet gerçekten düşmüşse,
bütün istemcilerin denemeye devam etmesi yükü artırır ve toparlanmayı geciktirir.

Bu nedenle uygulamalarda deneme, bir üst düzenekle sınırlandırılır: art arda başarısızlık
belirli bir eşiği geçtiğinde istekler bir süre hiç gönderilmez, sonra tek bir yoklama
isteğiyle durum sınanır. Aynı düşüncenin bir başka biçimi, aynı anda süren toplam deneme
sayısına sınır koymaktır.

Ölçüm akışı açısından sonuç şudur: her istasyon için birkaç deneme yapılır, kalıcı hatada
hemen vazgeçilir, bütçe dolduğunda istasyon "veri yok" olarak işaretlenir ve toplama
sürer. `allSettled` ile kurulan dayanıklı toplama, bu davranışın doğal kabıdır.

## Özet

- Yeniden deneme yalnız geçici hatalarda anlamlıdır; kalıcı hatada hem çağıran bekletilir
  hem sunucu boşuna meşgul edilir.
- Bekleme süresi her denemede artırılır; sabit aralıklı denemeler aşırı yüklü bir hizmete
  ikinci dalga olarak çarpar.
- Deneme sayısı ve toplam süre sınırlanmalı, bütçe dolduğunda hata kök nedeniyle birlikte
  yukarı verilmelidir.
- Yan etkili işlemler ancak yinelemeye dayanıklıysa yeniden denenebilir; dayanıklılık
  işlem anahtarıyla sağlanır.
- Yeniden deneme tek isteği kurtarır; sistem düzeyinde ayrıca bir sınırlama düzeneği
  gerekir.

## Sonraki Adım

Ağ konusu tamamlandı: istek yapıldı, sınırlandı, iptal edildi ve dayanıklı hale getirildi.
Bu derslerde birkaç kez "bellekte kalır" denildi ama neyin, neden ve ne kadar süreyle
kaldığı açıklanmadı. Sonraki konu bunu ele alır: belleğin ayrılma ve serbest bırakılma
döngüsü, erişilebilirlik temelli toplama, eşzamansız kodun bellekte tuttuğu şeyler ve
sızıntının araçlarla tanısı.
