JavaScript debounce ve throttle farkı: hangisi ne zaman kullanılır?
Arama alanında debounce, scroll olayında throttle kullanın; leading, trailing, maxWait, cancel ve eski API yanıtlarını yönetme örneklerini inceleyin.
Debounce, sık gelen olayların ardından belirli bir süre sessizlik oluşmasını bekler. Throttle ise olaylar devam ederken fonksiyonun belirli aralıklarla çalışmasına izin verir. Arama kutusunda genellikle debounce, scroll konumu takibinde ise throttle seçilir.
İki yöntem de yoğun olay akışının oluşturduğu işi azaltabilir. Ancak davranışları farklıdır: kullanıcının son değerini mi bekleyeceksiniz, yoksa hareket sürerken düzenli güncelleme mi yapacaksınız? Seçimi bu soruyla yapmak, yalnızca isimleri ezberlemekten daha işe yarar.
Aynı olay dizisinde sonuç nasıl değişir?
Olayların 0, 80, 160 ve 240. milisaniyede geldiğini, bekleme aralığının 300 ms olduğunu düşünün. Trailing debounce, yeni olay geldikçe beklemeyi yeniler ve son olaydan yaklaşık 300 ms sonra son değerle çalışır.
Leading ve trailing açık throttle ise ilk olayı hemen işleyebilir; bu süre içindeki diğer olayları tek tek çalıştırmaz ve aralık sonunda son değer için trailing çağrı yapabilir. Zamanlayıcıların gerçek çalışma zamanı event loop ve arka plan sekmesi kısıtlarından etkilenir; bu mekanizmalar kesin zamanlı saat değildir.
| İhtiyaç | Başlangıç seçimi | Neden? |
|---|---|---|
| Yazı bittikten sonra arama | Debounce | Ara sorguları bekletir. |
| Scroll sırasında düzenli konum okuma | Throttle | Olay akışı sürerken günceller. |
| Değişiklik sonrası otomatik kayıt | Debounce + maxWait | Sessizlik bekler; sürekli düzenlemede sonsuza kadar ertelenmez. |
| Animasyon kareleriyle görsel güncelleme | requestAnimationFrame | Görsel işi tarayıcı kareleriyle eşler. |
1. Lodash ile arama alanına debounce ekleyin
Örnekler, lodash kurulu ve modül import'unu destekleyen bundler projesi içindir. Saf tarayıcı modülü bu paket adını kendiliğinden çözmez. İlgili HTML'de id="search" olan input ve id="results" olan sonuç alanı bulunmalıdır.
import debounce from "lodash/debounce.js";
const input = document.querySelector("#search");
const results = document.querySelector("#results");
let revision = 0;
let controller;
const search = debounce(async (query, requestRevision) => {
controller = new AbortController();
try {
const response = await fetch(
`/api/search?q=${encodeURIComponent(query)}`,
{ signal: controller.signal }
);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
if (requestRevision !== revision) return;
// Ornek API sozlesmesi: { items: [{ title: "..." }] }
results.textContent = data.items.map(item => item.title).join(", ");
} catch (error) {
if (error.name !== "AbortError" && requestRevision === revision) {
results.textContent = "Arama tamamlanamadı.";
}
}
}, 300);
function onInput(event) {
const query = event.target.value.trim();
revision += 1;
controller?.abort();
if (query.length < 2) {
search.cancel();
results.textContent = "";
return;
}
search(query, revision);
}
input.addEventListener("input", onInput);
// Ekran kaldirildiginda lifecycle tarafindan cagrilir.
function disposeSearch() {
input.removeEventListener("input", onInput);
revision += 1;
search.cancel();
controller?.abort();
}
Debounce yalnızca çağrı sıklığını yönetir; başlamış ağ isteğini iptal etmez. Yukarıdaki AbortController önceki isteği durdurmaya çalışır. Revision kontrolü ise iptal edilmeden tamamlanan eski yanıtın yeni arama sonucunu ezmesini engeller. Kullanıcı alanı boşalttığında hem bekleyen çağrı hem önceki istek temizlenir.
2. Scroll olayında throttle kullanın
import throttle from "lodash/throttle.js";
const position = document.querySelector("#position");
const onScroll = throttle(() => {
position.textContent = `${Math.round(window.scrollY)} px`;
}, 200, { leading: true, trailing: true });
window.addEventListener("scroll", onScroll, { passive: true });
function disposeScroll() {
window.removeEventListener("scroll", onScroll);
onScroll.cancel();
}
Bu örnekte id="position" olan bir alan gerekir. Passive seçeneği dinleyicinin preventDefault kullanmayacağını belirtir; fonksiyonun yaptığı pahalı işi otomatik ucuzlatmaz. Her çağrıda yoğun DOM sorguları veya büyük hesaplar yapmak yine performansı etkileyebilir.
3. leading, trailing ve maxWait ne zaman gerekir?
leading bekleme aralığının başında, trailing sonunda çağrı davranışını kontrol eder. Lodash debounce varsayılan olarak trailing çalışır; throttle varsayılan olarak ikisini de açar. İkisi birlikte açıkken tek bir olayın mutlaka iki çağrı üreteceğini varsaymayın; aralık içindeki tekrarlar davranışı etkiler.
maxWait, debounce için sürekli gelen olayların çalışmayı sonsuza kadar ertelemesini önleyen üst bekleme sınırıdır. Örneğin otomatik kayıtta 500 ms sessizlik ve en fazla 5 saniye bekleme düşünülebilir. Bu değerler ürünün davranışına göre seçilmelidir; otomatik kayıtta hata yönetimi ve kayıt sıralaması da ayrıca gerekir.
4. Ekran kapanırken bekleyen işi temizleyin
cancel() bekleyen çağrıyı kaldırır. flush() bekleyen trailing çağrıyı hemen çalıştırır. Sayfa kapanırken ağ isteğinin tamamlanacağını varsayarak flush'a güvenmeyin; otomatik kayıt için uygulamanın yaşam döngüsü ve gerekirse farklı teslim yöntemi planlanmalıdır.
React veya başka bir UI framework'ünde debounced fonksiyonu her render'da yeniden oluşturmak birbirinden bağımsız zamanlayıcılar yaratabilir. Fonksiyon kimliğini kararlı tutun, güncel state'i nasıl okuyacağını belirleyin ve unmount cleanup'ında iptal edin.
Kısa rehber
Sık yapılan hata
Debounce'u çift ödeme veya çift form gönderimi için tek güvence saymak. Bu durumda buton durumu, sunucu doğrulaması ve gerekirse idempotency mekanizması da gerekir.