0. Closure + HOC
- 클로저: 이벤트가 발생할 때마다 이전의 상태를 기억해야 하기 때문에 클로저가 필요. 반환된 함수가 실행될 때마다 클로저를 사용하여 함수 실행여부를 결정할 수 있음
- 고차 함수: 인자로 실행하려는 함수를 받아, 이 함수를 조건에 따라 실행하는 새로운 함수를 반환
1. Debouncing
type Debounce<T extends unknown[]> = (...args: T) => void;
function debounce<T extends unknown[]>(func: (...args: T) => void, delay: number): Debounce<T> {
let timer: ReturnType<typeof setTimeout> | null = null;
return (...args: T) => {
if (timer !== null) clearTimeout(timer);
timer = setTimeout(() => {
func(...args);
}, delay);
};
}
// HOC 에 함수와 딜레이를 넣어서 호출
const handleInput: Debounce = debounce(() => {
console.log('API 호출');
}, 300);
document.querySelector('input').addEventListener('input', handleInput);
2. Throttling
type Throttle = (...args: any[]) => void;
// 콜백함수와 리미트를 인자로 받는 HOC
const throttle = (func: (...args: any[]) => void, limit: number): Throttle => {
let lastTimer: NodeJS.Timeout | null = null; // 클로저로 유지될 변수(타이머)
let lastRan: number | null = null; // 클로저로 유지될 변수(마지막 실행시점)
return (...args: any[]) => {
if(lastRan !== null){
func(...args); // 처음 호출될 때 바로 실행
lastRan = Date.now(); // 마지막 실행 시점을 현재 시간으로 기록
return;
}
if(lastTimer !== null) clearTimeout(lastTimer); // 이전에 설정된 타이머 취소
lastTimer = setTimeout(() => {
if((Date.now() - lastRan) >= limit) { // 제한 시간 이후에만 실행
func(...args); // 콜백함수 실행
lastRan = Date.now(); // 마지막 실행 시점 업데이트
}
}, limit - (Date.now() - lastRan))
// 위 setTimeout의 2번째 인자 부분은,
// 마지막으로 함수가 실행된 시점(lastRan)에서 얼마나 시간이 지났는지 계산하여,
// 정확히 limit 간격만큼 대기한 후에 함수를 실행시키기 위한 계산임
};
}
// HOC 에 함수와 리미트를 넣어서 호출
const handleScroll: Throttle = throttle(() => {
console.log("스크롤 이벤트 처리");
}, 1000);
window.addEventListener("scroll", handleScroll);