İçeriğe geç

Form Gönderimi — Bir Tıkla İki Havale Nasıl Olur?

Orta 8 dk Sık karşılaşılır

Önce şunu oku: JavaScript Olay Döngüsü — setTimeout(0) Neden Sonra Yazıyor?

30 saniyede özet

Kullanıcı bir kez gönderir ama sunucuya iki istek gider: çift tıklamadan ya da kaybolan bir cevaptan sonraki tekrar denemeden. Kilit ilkini durdurur; ikisini birden durduran şey idempotency anahtarıdır.

Kapıdaki zile heyecanla iki kez basarsın ve içeriden iki kişi koşar. Ya da zili çalarsın, ses gelmez, bir daha çalarsın; meğer ilkinde duymuşlardı. Bir form da tam olarak böyle iki kez gönderilir.

  1. Bayt: Havale formu hazır. Gönder'e basınca istek gidiyor, her şey yolunda.

  2. Sen: Bir müşteri aynı havalenin iki kez yapıldığını söylüyor.

  3. Bayt: Ama tek düğme var, tek bir fetch çağrısı!

  4. Bayt: Bir düğme bir istek demek değil. İkinci isteğin nereden geldiğine bakalım.

Bir düğme, iki istek

En bilinen yol çift tıklamadır. İlk istek dönmeden kullanıcı bir daha tıklar ve kod ikinci bir istek gönderir. Buna double submitTek bir gönderimin sunucuya iki istek olarak ulaşması: çift tıklama ya da kaybolan cevabın ardından tekrar deneme. Sunucu tarafında idempotency anahtarıyla durdurulur.Sözlükte gör → denir.

Daha sinsi yol ağdır. Sunucu havaleyi yapar ama cevap yolda kaybolur; istemci zaman aşımına düşüp aynı isteği tekrar dener.

Kafam karıştı, daha basit anlat

Bir gönderim iki istek olabilir: ya kullanıcı iki kez tıklar ya da kaybolan bir cevabın ardından istek tekrarlanır.

Hızlı kontrolBaşlangıç

Kullanıcı Gönder'e çift tıkladı ve kodda hiçbir koruma yok. Sunucuya kaç istek gider?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Sunucu havaleyi yaptı ama cevap yolda kayboldu. İstemci ne görür?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Kilit neyi durdurur?

Gönder düğmesi istek dönene kadar kilitleniyor. Kullanıcı bir kez tıkladı; sunucu havaleyi yaptı ama cevap kayboldu ve istemci aynı isteği tekrar denedi. Kaç havale olur? Cevabı göster

İki. Kilit ikinci tıklamayı durdurur, ama tekrar deneme bir tıklama değildir. Sunucu iki isteği iki ayrı havale sanar.

Zil kilidi ikinci basışı durdurur; tekrar gelen kuryeyi fiş numarası durdurur.
Adım adım oku
  1. Zile iki kez basıldı: iki paket gönderildi.
  2. Zil kilitliyken ikinci basış yok sayıldı.
  3. Cevap kayboldu; kurye aynı paketle tekrar geldi.
  4. Fiş numarası tanındı: bu paket zaten gönderilmişti.

Çözüm sunucu tarafında: her gönderim bir idempotencyAynı işlemin bir kez de on kez de çalışsa aynı sonucu vermesi. Dağıtık sistemde timeout aldığında işlemin yapılıp yapılmadığını bilemediğin için zorunludur.Sözlükte gör → anahtarı taşır. Anahtar form ilk kez gönderilirken bir kez üretilir ve her tekrar denemede aynı kalır.

Sunucu bir anahtarı en fazla bir kez işler. Aynı anahtarla ikinci istek gelirse yeni havale açmaz, ilkinin sonucunu döndürür. Sunucunun bu tarafı REST ve idempotency dersinde.

Kafam karıştı, daha basit anlat

Kilit kullanıcıyı, anahtar ağı durdurur. Para gibi bir işte ikisi birlikte gerekir.

Hızlı kontrolOrta

İstek beklerken düğme kilitli. Cevap kayboldu ve istemci aynı isteği tekrar denedi. Kaç havale olur?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Idempotency anahtarı sunucuda nasıl kullanılır?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Kendin gör

Bir gönderim, kaç havale?

Tohum 229479

Oynat ya da adımla.

Hız
Adım 0

Şu an ne oldu?

Kullanıcı çift tıklıyor

Kullanıcı bir kez göndermek istiyor. Sunucuya kaç istek gidecek, kaç havale olacak?

Görevler0/3

  • Çift tıkla iki havale yapaçık

    İpucu

    Varsayılan ayarlar yeter.

  • Düğme kilitliyken bile iki havale yapaçık

    İpucu

    Kilidin göremediği bir tekrar var.

  • Cevap kaybolsa da tek havaleaçık

    İpucu

    Sunucunun iki isteği aynı gönderim olarak tanıması gerek.

Olay günlüğü (0)

Henüz olay yok. Oynat veya adımla.

  1. Varsayılanla oynat. Çift tıklama, iki istek, iki havale.
  2. Düğmeyi kilitle. İkinci tık yok sayıldı, tek havale.
  3. Senaryoyu “cevap kayboluyor” yap. Kilit açıkken bile iki havale.
  4. Idempotency anahtarını aç. İki istek gitti, tek havale oldu.
Hızlı kontrolOrta

Idempotency anahtarı fetch çağrısının içinde crypto.randomUUID() ile üretiliyor. Tekrar denemede ne olur?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Idempotency anahtarı açıkken kullanıcı çift tıkladı ve düğme kilitli değil. Kaç havale olur?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Tuzaklar

Her denemede yeni anahtar. Anahtarı fetch çağrısının içinde üretirsen her tekrar deneme yeni bir anahtar taşır. Sunucu yine iki ayrı istek görür.

Anahtarı hiç yenilememek. Kullanıcı ikinci, gerçekten yeni bir havale yaptığında da aynı anahtar giderse sunucu onu da eski sanar. Başarılı bir gönderimden sonra yeni anahtar üret.

Yalnızca kilide güvenmek. Kilit tarayıcıdadır; sekme yenilenince, ağ tekrar deneyince ya da başka bir istemci çağırınca hiçbir şey yapmaz.

Kafam karıştı, daha basit anlat

Anahtar gönderim başına bir kez üretilir, tekrar denemede aynı kalır, başarıdan sonra yenilenir.

Hızlı kontrolOrta

Anahtar oturum başında bir kez üretiliyor ve hiç yenilenmiyor. Kullanıcı ikinci, gerçekten yeni bir havale yapınca ne olur?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Aşağıdaki örnek bir bankacılık uygulamasının havale formundan: düğme beklerken kilitli, her gönderim tek bir anahtar taşıyor, tekrar denemeler aynı anahtarla gidiyor.

Derinleş · Havale formu: kilit ve idempotency anahtarı birlikte 4 dosya · ~66 satır · ilk okumada atlayabilirsin
Proje dosyaları

src/features/transfer/ useTransferSubmit.ts Hook: anahtar gönderim başına bir kez üretiliyor, tekrar denemelerde aynı kalıyor, başarıdan sonra yenileniyor.

src/features/transfer/useTransferSubmit.ts
export function useTransferSubmit(send: (body: TransferBody, key: string) => Promise<TransferResult>) {
// One key per submission: created lazily, reused by every retry, renewed after success.
const keyRef = useRef<string | null>(null);
const pendingRef = useRef(false);
const [pending, setPending] = useState(false);
async function submit(body: TransferBody): Promise<TransferResult | undefined> {
if (pendingRef.current) return undefined; // a second click while the first is in flight
pendingRef.current = true;
setPending(true);
keyRef.current ??= crypto.randomUUID();
try {
const result = await send(body, keyRef.current);
keyRef.current = null; // the next transfer is a new submission
return result;
} finally {
pendingRef.current = false;
setPending(false);
}
}
return { submit, pending };
}

src/features/transfer/ TransferForm.tsx Form: istek beklerken düğme kilitli; ref ile tutulan bayrak aynı anda gelen iki tıkı da durduruyor.

src/features/transfer/TransferForm.tsx
export function TransferForm({ send }: { send: SendTransfer }) {
const { submit, pending } = useTransferSubmit(send);
return (
<form
onSubmit={(event) => {
event.preventDefault();
void submit(readForm(event.currentTarget));
}}
>
{/* fields … */}
<button type="submit" disabled={pending} aria-busy={pending}>
{pending ? 'Gönderiliyor…' : 'Gönder'}
</button>
</form>
);
}

src/features/transfer/ sendTransfer.ts İstemci: zaman aşımında aynı anahtarla en fazla iki kez daha deniyor.

src/features/transfer/sendTransfer.ts
// Retries reuse the same Idempotency-Key, so the server books the transfer at most once.
export async function sendTransfer(body: TransferBody, key: string, attempts = 3): Promise<TransferResult> {
for (let attempt = 1; ; attempt += 1) {
try {
const response = await fetch('/api/transfers', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Idempotency-Key': key },
body: JSON.stringify(body),
signal: AbortSignal.timeout(8_000),
});
if (!response.ok) throw new TransferError(response.status);
return (await response.json()) as TransferResult;
} catch (error) {
if (attempt >= attempts || error instanceof TransferError) throw error;
}
}
}

src/features/transfer/ TransferForm.test.tsx Test: iki hızlı tık yalnızca bir istek gönderiyor.

src/features/transfer/TransferForm.test.tsx
it('sends one request for a fast double click', async () => {
const send = vi.fn(() => new Promise<TransferResult>(() => {})); // never resolves
render(<TransferForm send={send} />);
const button = screen.getByRole('button', { name: 'Gönder' });
await userEvent.dblClick(button);
expect(send).toHaveBeenCalledTimes(1);
});

Kendini sına

Şimşek turu1/4

Tek bir Gönder düğmesi sunucuya en fazla bir istek gönderebilir.

Soru 1/3İleri

React'te setPending(true) ile düğme disabled yapılıyor. Neden bir ref bayrağı da eklenir?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Aklında kalacak üç şey

  1. 1 Bir gönderim iki istek olabilir: çift tıklama ya da cevabı kaybolan isteğin tekrar denenmesi. İkisi de sunucuda iki ayrı işlem gibi görünür.
  2. 2 İstek beklerken düğmeyi kilitlemek çift tıklamayı durdurur, ama aynı gönderimin tekrar denenmesini durduramaz.
  3. 3 Her gönderimde bir kez üretilen ve her denemede aynı kalan bir idempotency anahtarı, sunucunun aynı işi ikinci kez yapmamasını sağlar.
Sonraki kapı Aynı istek Postman'de çalışıyor, tarayıcıda 'CORS error' veriyor. Sunucu mu bozuk, tarayıcı mı? CORS — Postman'de Çalışıyor, Tarayıcıda Neden Hata? · 9 dk

4 kart sonraki derste seni bekliyor

0/4 kart bu dersten toplandı