Throttle
Throttle texnikasi va debounce'dan farqi, timestamp-based va timer-based (trailing) implementatsiyalar, Event Loop/rendering bilan bog'liqligi, requestAnimationFrame asosidagi throttle, va qachon debounce, qachon throttle ishlatish kerakligi.
67-dars
Throttle
Throttle — bir funksiyaning tez-tez chaqirilishini cheklab, uni ma'lum vaqt oralig'ida ko'pi bilan bir marta ishga tushirish texnikasi — debounce'dan farqli, chaqiruvlar davom etayotgan bo'lsa ham, funksiya muntazam ravishda ishlab turadi, "tinchlikni" kutmaydi.
function throttle(fn, oraliq) {
let oxirgiVaqt = 0;
return function (...args) {
const hozir = Date.now();
if (hozir - oxirgiVaqt >= oraliq) {
oxirgiVaqt = hozir;
fn.apply(this, args);
}
};
}
const progressYangilash = throttle(() => {
console.log("Scroll progress yangilandi:", window.scrollY);
}, 200);
window.addEventListener('scroll', progressYangilash);
Foydalanuvchi uzluksiz scroll qilayotganda, scroll hodisasi sekundiga o'nlab marta ishga tushadi. Throttle bilan — funksiya har 200ms'da ko'pi bilan bir marta chaqiriladi, garchi scroll uzluksiz davom etayotgan bo'lsa ham.
1. Debounce bilan solishtirish — bu yerda eng ko'p chalkashadigan joy
Bu ikkisi tashqi ko'rinishda o'xshash (ikkalasi ham "tez-tez chaqiriluvchi funksiyani cheklaydi"), lekin maqsad va natija butunlay farq qiladi:
Chaqiruvlar vaqti: |--|--|--|--|--|--|--|--|--|--|--| (uzluksiz, 50ms oraliqda)
0 50 100 150 200 250 300 350 400 450 500
DEBOUNCE (500ms): Hech narsa bajarilmaydi, chaqiruvlar davom etyapti...
...faqat OXIRGI chaqiruvdan 500ms tinchlikdan keyin — 1 marta ishlaydi
THROTTLE (200ms): t=0'da ishlaydi
t=200'da ishlaydi
t=400'da ishlaydi
(chaqiruvlar davom etsa ham, MUNTAZAM oraliqda ishlab turadi)
Debounce — "faqat foydalanuvchi to'xtagach bir marta bajar" (masalan, qidiruv so'rovi — oraliq natija kerak emas).
Throttle — "foydalanuvchi harakat qilayotganida ham, muntazam vaqt oralig'ida bajarib tur" (masalan, scroll paytida progress bar — foydalanuvchi ko'rib turishi kerak, faqat oxirida emas).
Agar scroll uchun debounce ishlatilsa — progress bar foydalanuvchi scroll qilib bo'lgandan keyingina bir marta yangilanadi, bu esa foydalanuvchi tajribasini yomonlashtiradi (u scroll qilib borayotganda hech narsa yangilanmayotganini ko'radi).
2. Ichki mexanizm — timestamp asosida (yuqoridagi versiya)
Yuqoridagi implementatsiya — "timestamp-based throttle":
- Har chaqiruvda joriy vaqt (
Date.now()) oxirgi bajarilgan vaqt bilan solishtiriladi - Agar farq belgilangan oraliqdan katta yoki teng bo'lsa — funksiya darhol bajariladi va
oxirgiVaqtyangilanadi - Aks holda — chaqiruv butunlay e'tiborsiz qoldiriladi (hech qanday timer rejalashtirilmaydi)
Bu — "leading" throttle: birinchi chaqiruv darhol ishlaydi, keyingi chaqiruvlar oraliq tugagunicha e'tiborsiz.
Muammosi: agar chaqiruvlar oraliq o'rtasida to'xtab qolsa (masalan, foydalanuvchi scroll qilib, t=150ms'da to'xtasa), oxirgi holat (t=150ms'dagi scroll pozitsiyasi) hech qachon ishlatilmaydi — chunki keyingi chaqiruv yo'q, funksiya faqat kelgusi chaqiruv bo'lganda ishga tushadi.
3. Timer-based throttle (trailing bilan)
Buni tuzatish uchun setTimeout asosidagi versiya ishlatiladi — bu debounce bilan ko'proq umumiylikka ega:
function throttle(fn, oraliq) {
let kutilyapti = false;
let oxirgiArgs = null;
return function (...args) {
oxirgiArgs = args;
if (!kutilyapti) {
kutilyapti = true;
setTimeout(() => {
fn.apply(this, oxirgiArgs); // OXIRGI holat bilan ishga tushadi
kutilyapti = false;
}, oraliq);
}
};
}
Bu yerda mantiq: birinchi chaqiruv timer'ni ishga tushiradi (kutilyapti = true). Timer tugaguncha kelgan barcha keyingi chaqiruvlar faqat oxirgiArgsni yangilaydi, lekin yangi timer yaratmaydi. Timer tugaganda — eng oxirgi argumentlar bilan fn ishga tushadi, va navbat qayta ochiladi. Bu — leading + trailing kombinatsiyasi: birinchi chaqiruv ham, oraliqdagi eng so'nggi holat ham hisobga olinadi.
4. Event Loop nuqtai nazaridan
scroll hodisasi — brauzer tomonidan juda tez-tez, ba'zan requestAnimationFrame tsikliga bog'liq holda ishga tushiriladi. Har bir hodisa handler chaqiruvi — Call Stack'da ishlaydigan sinxron kod. Throttle'siz, agar handler og'ir bo'lsa (masalan, DOM'ni o'qish — Reflow'ni majburlash), bu Call Stack'ni tez-tez band qilib, brauzerning rendering pipeline'iga (frame chizishga) vaqt qoldirmaydi — natija: "jerky" (silliq bo'lmagan) scroll, past FPS.
Throttle chaqiruvlar sonini kamaytirish orqali Call Stack'ni tezroq bo'shatadi, brauzerga har frame'da render qilish uchun ko'proq vaqt qoldiradi.
Muhim maslahat: vizual yangilanishlar (masalan, scroll asosida animatsiya) uchun setTimeout/timestamp asosidagi throttle o'rniga ko'pincha requestAnimationFrame ishlatish tavsiya etiladi — chunki u brauzerning rendering tsikli bilan sinxron ishlaydi (sekundiga taxminan 60 marta, ekran yangilanish chastotasiga mos), va hech qachon keraksiz ravishda tez-tez yoki kam-kam ishlamaydi.
function throttleRAF(fn) {
let kutilyapti = false;
return function (...args) {
if (!kutilyapti) {
kutilyapti = true;
requestAnimationFrame(() => {
fn.apply(this, args);
kutilyapti = false;
});
}
};
}
5. V8/GC nuqtai nazaridan — bog'liqlik
Debounce'da ko'rganimizdek, throttle ham closure orqali holatni (oxirgiVaqt, kutilyapti, oxirgiArgs) saqlaydi. Agar throttle'langan funksiya scroll/resize kabi global, uzoq yashovchi hodisaga bog'lansa va removeEventListener bilan hech qachon tozalanmasa — bu closure (va u ushlab turgan barcha o'zgaruvchilar) memory leak manbai bo'lib qolishi mumkin (aynan oldingi mavzuda ko'rgan "unutilgan event listener" holati).
Qachon nimasi ishlatiladi (amaliy xulosa)
| Holat | Texnika | Sabab |
|---|---|---|
| Search input | Debounce | Faqat yozish tugagach so'rov kerak |
| Scroll'da progress bar | Throttle | Muntazam yangilanish ko'rinishi kerak |
| Infinite scroll (pastga yetganda yuklash) | Throttle | Muntazam tekshirish, lekin tez-tez emas |
| Button "double-click" himoyasi | Throttle (leading) | Birinchi bosish darhol, qolgani e'tiborsiz |
| Window resize'da layout hisoblash | Debounce ko'pincha, ba'zan Throttle | Foydalanuvchi tanlaviga bog'liq |
| Drag-and-drop pozitsiya yangilash | Throttle (RAF bilan) | Silliq vizual yangilanish kerak |