Programming/JS

[Javascript] 타이머 함수

w00se 2022. 2. 21. 23:53

호출 스케줄링 - 함수의 실행을 예약하는 방법

  • 타이머 함수를 이용하면 일정 시간이 지난 후 특정 함수를 실행 가능하다.

 

타이머 함수

  • javascript에서는 대표적인 타이머 함수는 setTimeoutsetInterval 가 있다.

 

setTimeout - 일정 시간이 지난 후 콜백 함수를 한번 호출

  • setTimeout은 단 한번 동작하는 타이머 생성
  • 타이머가 만료되면 전달받은 콜백 함수를 호출
  • MDN - setTimeout
  • setTimeout의 반환 값은 생성한 타이머에 대한 고유 id 값
    • 타이머 고유 id 값은 실행 환경에 따라 다르다.
  • 반환 값에 대한 예시 코드 및 결과
const firstTimerId = setTimeout(() => {
  console.log('first callback 함수 호출');
}, 1000);

console.log('firstTimerId: ', firstTimerId);

const secondTimerId = setTimeout(() => {
  console.log('second callback 함수 호출');
}, 1000);

console.log('secondTimerId: ', secondTimerId);

const thirdTimerId = setTimeout(() => {
  console.log('third callback 함수 호출');
}, 1000);

console.log('thirdTimerId: ', thirdTimerId);
  • 브라우저
    • 타이머 id는 정수 값이며 1부터 시작하는 걸로 추측됨
  • Node
    • 타이머 id는 객체

 

setInterval - 일정 시간마다 콜백 함수를 호출

  • setInterval은 일정 시간마다 만료되는(반복 동작하는) 타이머 생성
  • 이 역시 타이머가 만료되면 전달받은 콜백 함수를 호출
  • MDN - setInterval
  • setInterval의 반환 값 역시 생성한 타이머에 대한 고유 id 값이다.
    • setTimeout과 마찬가지로 타이머 고유 id 값은 실행 환경에 따라 다르다.
  • setInterval 종료하는 코드 예시
let turn = 1;

const timerId = setInterval(() => {
  console.log('현재 turn: ', turn);

  if (turn === 10) {
    clearInterval(timerId);
  }

  turn = turn + 1;
}, 1000);

// 출력 예시
// 현재 turn: 1
// 현재 turn: 2
// 현재 turn: 3
// ...
// ...
// 현재 turn: 9
// 현재 turn: 10

 

Debounce(디바운스)와 Throttle(스로틀) - setTimeout을 이용해서 이벤트의 빈번한 호출을 방지

🤔 왜 필요한가?

  • 이벤트 핸들러에 등록된 작업이 무거운 작업일 때, 이벤트의 과도환 호출은 성능 저하를 초래할 수 있다.
  • 이런 경우 디바운스와 스로틀을 사용하면 성능 저하를 예방할 수 있다.

예제 코드 및 구현 결과

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <title>Timer 연습</title>
  </head>
  <body>
    <h1>Timer 연습</h1>
    <button id="button">버튼</button>
    <div>
      일반 클릭 이벤트 카운터
      <span id="normal_counter"> 0 </span>
    </div>
    <div>
      디바운스 클릭 이벤트 카운터
      <span id="debounce_counter"> 0 </span>
    </div>
    <div>
      스로틀 클릭 이벤트 카운터
      <span id="throttle_counter"> 0 </span>
    </div>
    <script>
      const $button = document.querySelector('#button');
      const $normal_counter = document.querySelector('#normal_counter');
      const $debounce_counter = document.querySelector('#debounce_counter');
      const $throttle_counter = document.querySelector('#throttle_counter');

      const debounce = (callback, delay) => {
        let timerId;

        return (event) => {
          // delay가 지나기 전에 이벤트가 다시 발생하면 이전 timer는 취소됨
          // 따라서 delay까지 이벤트가 발생하지 않아야 callback 함수가 실행된다.
          if (timerId) clearTimeout(timerId);

          timerId = setTimeout(callback, delay, event);
        };
      };

      const throttle = (callback, delay) => {
        let timerId;

        return (event) => {
          // delay가 지나기 전에 이벤트가 다시 발생해도 새로운 타이머가 생성되지 않음
          // 따라서 delay 시간이 되면 callback이 호출되고 새로운 타이머가 생성된다.
          if (timerId) return;

          timerId = setTimeout(
            () => {
              callback(event);
              timerId = null;
            },
            delay,
            event
          );
        };
      };

      $button.addEventListener('click', () => {
        $normal_counter.textContent = Number($normal_counter.textContent) + 1;
      });

      $button.addEventListener(
        'click',
        debounce(() => {
          $debounce_counter.textContent = Number($debounce_counter.textContent) + 1;
        }, 500)
      );

      $button.addEventListener(
        'click',
        throttle(() => {
          $throttle_counter.textContent = Number($throttle_counter.textContent) + 1;
        }, 500)
      );
    </script>
  </body>
</html>

 


참고 자료
MDN Docs
모던 Javascript Deep Dive - 이웅모 저