자바스크립트 엔진은 싱글 스레드로 동작한다.
타이머 함수는 비동기 처리 방식으로 동작한다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<button>click me</button>
<pre>일반 클릭 이벤트 카운터 <span class="normal-msg">0</span></pre>
<pre>디바이스 클릭 이벤트 카운터 <span class="debounce-msg">0</span></pre>
<pre>스로틀 클릭 이벤트 카운터 <span class="throttle-msg">0</span></pre>
<script>
const $button = document.querySelector("button");
const $normalMsg = document.querySelector(".normal-msg");
const $debounceMsg = document.querySelector(".debounce-msg");
const $throttleMsg = document.querySelector(".throttle-msg");
const debounce = (callback, delay) => {
let timeId;
return (...args) => {
if (timeId) clearTimeout(timeId);
timeId = setTimeout(callback, delay, ...args);
};
};
const throttle = (callback, delay) => {
let timeId;
return (...args) => {
if (timeId) return;
timeId = setTimeout(() => {
callback(...args);
timeId = null;
}, delay);
};
};
$button.addEventListener("click", () => {
$normalMsg.textContent = +$normalMsg.textContent + 1;
});
$button.addEventListener(
"click",
debounce(() => {
$debounceMsg.textContent = +$debounceMsg.textContent + 1;
}, 500)
);
$button.addEventListener(
"click",
throttle(() => {
$throttleMsg.textContent = +$throttleMsg.textContent + 1;
}, 500)
);
</script>
</body>
</html>
짧은 시간 간격으로 이벤트가 연속해서 발생하면 이벤트 핸들러를 호줄하지 않다가 일정 시간이 경과한 이후에 이벤트 핸들러가 한 번만 호출되도록 한다.
짧은 시간 간격으로 발생하는 이벤트를 그룹화해서 마지막에 한 번만 이벤트 핸들러가 호출되도록 한다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<input type="text" name="" id="" />
<div class="msg"></div>
<script>
const $input = document.querySelector("input");
const $msg = document.querySelector(".msg");
const debounce = (callback, delay) => {
let timerId;
return (...arg) => {
console.log("...arg :", ...arg);
if (timerId) clearTimeout(timerId);
timerId = setTimeout(callback, delay, ...arg);
};
};
$input.oninput = debounce((e) => {
$msg.textContent = e.target.value;
}, 300);
</script>
</body>
</html>
스로틀은 짧은 시간 간격으로 이벤트가 연속해서 발생하더라도 일정 시간 간격으로 이벤트 핸들러가 최대 한 번만 호출되도록 한다.
스로틀은 짧은 시간 간격으로 연속해서 발생하는 이벤트를 그룹화해서 일정 시간 단위로 이벤트 핸들러가 호출되도록 호출 주기를 만든다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
.container {
width: 300px;
height: 300px;
background-color: rebeccapurple;
overflow: scroll;
}
.content {
width: 300px;
height: 100vh;
}
</style>
<title>Document</title>
</head>
<body>
<div class="container">
<div class="content"></div>
</div>
<div>
일반 이벤트 핸들러가 scroll 이벤트를 처리한 횟수:
<span class="normal-count">0</span>
</div>
<div>
스로틀 이벤트 핸들러가 scroll 이벤트를 처리한 횟수:
<span class="throttle-count">0</span>
</div>
<script>
const $container = document.querySelector(".container");
const $normalCount = document.querySelector(".normal-count");
const $throttleCount = document.querySelector(".throttle-count");
const throttle = (callback, delay) => {
let timerId;
return (...args) => {
if (timerId) return;
timerId = setTimeout(() => {
callback(...args);
timerId = null;
}, delay);
};
};
let normalCount = 0;
$container.addEventListener("scroll", () => {
$normalCount.textContent = ++normalCount;
});
let throttleCount = 0;
$container.addEventListener(
"scroll",
throttle(() => {
$throttleCount.textContent = ++throttleCount;
}, 100)
);
</script>
</body>
</html>