Debounce dan Throttle: Menjinakkan Event
Kenapa kolom search dan event scroll bisa membanjiri browser, dan cara mengatasinya dengan implementasi debounce dan throttle sendiri.
Kenapa Ini Penting
Bayangkan kolom pencarian yang memanggil API setiap kali pengguna mengetik satu huruf. Kata "sepatu" berarti 6 request beruntun, dan 5 di antaranya mubazir karena hasilnya langsung dibuang. Atau event scroll yang menyala puluhan kali per detik dan memaksa browser menghitung ulang layout setiap kali. Hasilnya: aplikasi lambat, kuota API jebol, dan baterai terkuras. Debounce dan throttle adalah dua pola standar untuk menjinakkan banjir event seperti ini.
Debounce: Tunggu Sampai Berhenti
Debounce menunda eksekusi sampai event BERHENTI datang selama jeda tertentu. Cocok untuk search-as-you-type: request hanya dikirim setelah pengguna berhenti mengetik.
function debounce(fn, jeda) {
let timer;
return function (...args) {
clearTimeout(timer); // batalkan jadwal sebelumnya
timer = setTimeout(() => fn.apply(this, args), jeda);
};
}
const cari = debounce((teks) => {
console.log("Request API untuk:", teks);
}, 300);
// simulasi pengguna mengetik "sepatu"
cari("s"); cari("se"); cari("sep"); cari("sepa"); cari("sepat");
cari("sepatu"); // hanya yang ini yang jalan, 300ms setelahnyaSetiap ketikan baru membatalkan timer sebelumnya, jadi hanya ketikan TERAKHIR yang dieksekusi. Analogi: seperti lift yang pintunya terus terbuka selama ada orang masuk, dan baru menutup setelah tidak ada yang masuk selama beberapa detik.
Throttle: Batasi Kecepatan
Throttle membatasi eksekusi menjadi maksimal sekali per interval, tidak peduli seberapa sering event datang. Cocok untuk scroll dan resize: update posisi boleh sering, tapi tidak perlu 60 kali per detik.
function throttle(fn, interval) {
let terakhir = 0;
return function (...args) {
const sekarang = Date.now();
if (sekarang - terakhir >= interval) {
terakhir = sekarang;
fn.apply(this, args);
}
};
}
const saatScroll = throttle(() => {
console.log("Posisi scroll:", window.scrollY);
}, 200);
window.addEventListener("scroll", saatScroll);Analogi: seperti keran air yang hanya menetes sekali tiap 200ms walau kamu memutarnya terus. Event yang datang di tengah interval dibuang begitu saja.
Debounce vs Throttle: Kapan Pakai Mana
Pakai debounce ketika kamu hanya peduli pada event TERAKHIR dalam rangkaian: pencarian, validasi form saat mengetik, auto-save. Pakai throttle ketika kamu perlu merespons SELAMA rangkaian berlangsung tapi dengan kecepatan dibatasi: animasi mengikuti scroll, update posisi mouse, tombol yang tidak boleh di-spam.
Contoh Nyata: Search Box
const input = document.querySelector("#cari");
const fetchHasil = debounce(async (q) => {
const res = await fetch("https://api.example.com/cari?q=" + q);
const data = await res.json();
tampilkan(data);
}, 300);
input.addEventListener("input", (e) => fetchHasil(e.target.value));Tanpa debounce, 10 huruf berarti 10 request. Dengan debounce 300ms, biasanya hanya 1 request yang benar-benar terkirim.
Jebakan Umum
Pertama, debounce yang dibuat di dalam render (misalnya di komponen React) akan membuat timer baru setiap render sehingga debounce tidak pernah bekerja: buat sekali di luar, atau simpan dengan useRef/useMemo. Kedua, jangan lupa membersihkan timer saat tidak dibutuhkan (clearTimeout / removeEventListener) agar tidak bocor memori. Ketiga, pilih jeda dengan akal sehat: 300ms nyaman untuk search, tapi 300ms untuk tombol terasa lemot, pakai throttle atau jeda lebih pendek.
Catatan teknis: Kedua pola ini mengandalkan closure: variabel
timerdanterakhirhidup di antara pemanggilan karena fungsi yang dikembalikan "mengingat" lingkupnya.fn.apply(this, args)menjaga agarthisdan semua argumen diteruskan dengan benar ke fungsi aslinya.
export default function App(): JSX.Element { return <h1>Hello world</h1> }
Tantangan
Auto-Save yang Hemat
Editor online nyata (Google Docs dan sejenisnya) menyimpan otomatis saat pengguna mengetik, tapi menyimpan ke server setiap satu huruf akan membanjiri backend. Tantanganmu: buat fungsi simpanOtomatis memakai debounce 1000ms sehingga fungsi simpan(teks) hanya dipanggil sekali, yaitu 1 detik setelah ketikan terakhir. Buktikan dengan memanggilnya 5 kali cepat beruntun lalu amati console: simpan harus tercatat tepat 1 kali.
<!doctype html> <html> <head> <meta charset="utf-8" /> </head> <body> <h1>Halo JS</h1> <p>Buka console preview untuk melihat output.</p> <script src="index.js"></script> </body> </html>