Giới thiệu
Trong các ứng dụng web, việc gắn nhiều listener vào các sự kiện như scroll, resize hay keyup có thể gây ra hàng nghìn lần gọi hàm trong một giây, làm giảm hiệu năng và gây giật lag. Hai kỹ thuật debounce và throttle giúp kiểm soát tần suất thực thi hàm, giảm tải cho trình duyệt và cải thiện trải nghiệm người dùng.
Debounce là gì?
Debounce trì hoãn việc thực thi hàm cho đến khi sự kiện ngừng xảy ra trong một khoảng thời gian xác định. Nếu sự kiện tiếp tục xảy ra trước khi thời gian chờ hết, bộ đếm sẽ được đặt lại, do đó hàm chỉ được gọi một lần duy nhất sau khi người dùng dừng thao tác.
Một ví dụ thực tế
Giả sử bạn muốn thực hiện tìm kiếm tự động khi người dùng nhập từ khóa. Thay vì gửi yêu cầu mỗi khi nhấn phím, bạn có thể debounce để chỉ gửi khi người dùng dừng nhập ít nhất 300 ms.
function debounce(func, wait) {
let timeout;
return function(...args) {
const context = this;
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(context, args);
}, wait);
};
}Sử dụng debounce trong JavaScript thuần:
const searchInput = document.getElementById('search');
searchInput.addEventListener('input', debounce(function(e) {
// Gửi yêu cầu AJAX ở đây
console.log('Tìm kiếm:', e.target.value);
}, 300));Throttle là gì?
Throttle giới hạn số lần thực thi hàm trong một khoảng thời gian cố định. Ngay cả khi sự kiện liên tục xảy ra, hàm sẽ chỉ được gọi một lần mỗi interval ms, giúp giảm tải CPU mà vẫn duy trì cập nhật giao diện.
Khi nào nên dùng Throttle?
Throttle thích hợp cho các sự kiện cần cập nhật liên tục nhưng không yêu cầu độ chính xác tuyệt đối, như scroll để thay đổi thanh điều hướng hoặc resize để tính lại layout.
function throttle(func, limit) {
let lastFunc;
let lastRan = Date.now();
return function(...args) {
const context = this;
const now = Date.now();
if (now - lastRan >= limit) {
func.apply(context, args);
lastRan = now;
} else {
clearTimeout(lastFunc);
lastFunc = setTimeout(() => {
func.apply(context, args);
lastRan = Date.now();
}, limit - (now - lastRan));
}
};
}Ví dụ áp dụng throttle cho sự kiện scroll:
window.addEventListener('scroll', throttle(function() {
console.log('Vị trí scroll:', window.scrollY);
}, 200));Áp dụng trong React
Trong các dự án React, bạn thường muốn debounce giá trị nhập để tránh render lại quá thường xuyên. Một hook tùy chỉnh useDebounce có thể giải quyết vấn đề này.
import { useState, useEffect } from 'react';
function useDebounce(value, delay) {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebounced(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debounced;
}Sử dụng hook trong component:
function SearchBox() {
const [query, setQuery] = useState('');
const debouncedQuery = useDebounce(query, 300);
useEffect(() => {
if (debouncedQuery) {
// Gửi yêu cầu API ở đây
console.log('Gửi yêu cầu tìm kiếm cho:', debouncedQuery);
}
}, [debouncedQuery]);
return (
<input
type="text"
value={query}
onChange={e => setQuery(e.target.value)}
placeholder="Nhập từ khóa..."
/>
);
}So sánh Debounce và Throttle
- Debounce thực thi hàm sau khi sự kiện dừng lại, phù hợp cho các thao tác nhập liệu, tìm kiếm, hoặc bất kỳ hành động nào cần chờ người dùng hoàn thành.
- Throttle thực thi hàm định kỳ trong khi sự kiện vẫn đang diễn ra, thích hợp cho các cập nhật UI liên tục như thay đổi vị trí scroll, resize cửa sổ, hoặc cập nhật biểu đồ thời gian thực.
Lưu ý khi triển khai
- Không đặt thời gian
waithoặclimitquá ngắn, vì sẽ mất hiệu quả tối ưu. - Đối với các hàm phụ thuộc vào
this, hãy chắc chắn giữ ngữ cảnh bằngapplyhoặcbind. - Trong môi trường React, nên dùng hook để quản lý debounce thay vì tạo hàm bên ngoài, giúp tránh việc tạo lại hàm mỗi lần render.
- Kiểm tra thực tế bằng DevTools: mở tab Performance và quan sát số lần gọi hàm trước và sau khi áp dụng.
Kiểm tra và đo lường hiệu năng
Để xác nhận việc giảm tải, bạn có thể mở Chrome DevTools, vào tab Performance và ghi lại hoạt động khi cuộn trang hoặc nhập liệu. So sánh số lần gọi hàm trước và sau khi áp dụng debounce/throttle sẽ cho thấy sự khác biệt rõ rệt, đồng thời thời gian phản hồi UI sẽ giảm đáng kể.
Kết luận
Việc áp dụng debounce và throttle một cách hợp lý sẽ cải thiện trải nghiệm người dùng, giảm tải CPU và tránh hiện tượng giật lag trong các ứng dụng web hiện đại. Nếu bạn muốn nắm vững các kỹ thuật này cùng với nhiều chủ đề nâng cao khác trong JavaScript, Tham khảo khóa học "JavaScript từ cơ bản đến nâng cao" tại đây.







