Để dừng một hàm đang chạy lặp lại (interval) hoặc chạy sau delay (timeout), lưu ID trả về từ setInterval/setTimeout và dùng clearInterval/clearTimeout.
Cơ chế:
// BẮT ĐẦU
const timerId = setInterval(callback, delayMs); // Lặp lại
const timeoutId = setTimeout(callback, delayMs); // Chạy 1 lần sau delay
// DỪNG
clearInterval(timerId); // Dừng interval
clearTimeout(timeoutId); // Hủy timeout chưa chạy
Ví dụ minh họa
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dừng hàm JS</title>
<style>
body { font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; padding: 20px; }
button { padding: 10px 20px; margin: 5px; cursor: pointer; font-size: 16px; }
#log { font-family: monospace; background: #f5f5f5; padding: 10px; height: 200px; overflow: auto; border: 1px solid #ddd; }
.running { background: #e8f5e9; }
.stopped { background: #ffebee; }
</style>
</head>
<body>
<h1>Dừng hàm đang thực thi với clearInterval</h1>
<button id="startBtn">BẮT ĐẦU (setInterval)</button>
<button id="stopBtn" disabled>DỪNG (clearInterval)</button>
<button id="clearBtn">XÓA LOG</button>
<div id="log"></div>
<script>
const startBtn = document.getElementById('startBtn');
const stopBtn = document.getElementById('stopBtn');
const clearBtn = document.getElementById('clearBtn');
const log = document.getElementById('log');
let intervalId = null;
let counter = 0;
function logMessage(msg, type = '') {
const div = document.createElement('div');
div.textContent = `[${new Date().toLocaleTimeString()}] ${msg}`;
if (type) div.className = type;
log.prepend(div);
}
startBtn.addEventListener('click', () => {
if (intervalId) return; // Đang chạy
counter = 0;
intervalId = setInterval(() => {
counter++;
logMessage(`Interval chạy lần ${counter}`, 'running');
}, 1000);
startBtn.disabled = true;
stopBtn.disabled = false;
});
stopBtn.addEventListener('click', () => {
if (intervalId) {
clearInterval(intervalId);
intervalId = null;
logMessage('⏹ Interval đã DỪNG', 'stopped');
startBtn.disabled = false;
stopBtn.disabled = true;
}
});
clearBtn.addEventListener('click', () => log.innerHTML = '');
</script>
</body>
</html>
Kết quả
Nhấn BẮT ĐẦU → Log in "Interval chạy lần 1, 2, 3..." mỗi giây.
Nhấn DỪNG → Log "⏹ Interval đã DỪNG", interval dừng hẳn.
Các trường hợp sử dụng phổ biến:
| Tình huống | API | Dừng bằng |
|---|---|---|
| Lặp lại định kỳ (polling, animation, clock) | setInterval | clearInterval(id) |
| Chạy 1 lần sau delay (debounce, timeout) | setTimeout | clearTimeout(id) |
| Animation frame (60fps) | requestAnimationFrame | cancelAnimationFrame(id) |
| Idle callback | requestIdleCallback | cancelIdleCallback(id) |
Pattern phổ biến: Debounce (trì hoãn thực thi)
function debounce(fn, delay) {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId); // Hủy lần trước
timeoutId = setTimeout(() => fn(...args), delay);
};
}
// Usage: chỉ search khi user ngừng gõ 300ms
const search = debounce((query) => api.search(query), 300);
input.addEventListener('input', e => search(e.target.value));
Pattern: Throttle (giới hạn tần suất)
function throttle(fn, limit) {
let inThrottle;
return (...args) => {
if (!inThrottle) {
fn(...args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
// Usage: scroll handler tối đa 1 lần/100ms
window.addEventListener('scroll', throttle(() => console.log('scroll'), 100));
⚠️ Lưu ý quan trọng:
- Luôn lưu ID trả về để clear sau này
- clearInterval/clearTimeout an toàn nếu ID null/undefined (không throw)
- Trong React: dùng useEffect cleanup:
return () => clearInterval(id) - Memory leak: quên clear interval khi component unmount