티스토리 뷰

41장. 타이머

41-1. 호출 스케줄링

  • 일정 시간이 경과된 이후에 호출되도록 함수 호출을 예약하려면 타이머 함수를 사용한다.
  • 이를 호출 스케줄링(scheduling a call) 이라 한다.
  • 타이머 함수는 ECMAScript 사양에 정의된 빌트인 함수 아니다.
  • 브라우저 환경과 Node.js 환경에서 모두 전역 객체의 메서드로서 타이머 함수를 제공한다.
  • JS 엔진은 단 하나의 실행 컨텍스트 스택을 갖기 때문에 두 가지 이상의 태스크 동시 실행할 수 없다.
  • 싱글 스레드로 동작한다.
  • 이런 이유로 타이머함수는 비동기 처리방식으로 동작한다.

41-2. 타이머 함수

  • delay ms 단위
  • delay default: 0
  • 타이머가 만료되었다고 콜백 함수 즉시 호출을 보장하진 않는다.
  • 태스크 큐에 콜백 함수를 등록하는 시간을 지연할 뿐
  • delay 4ms 이하인 경우 최소 지연 시간 4ms 지정됨.
// setTimeout
const timerId = setTimeout(name => console.log(`Hi! ${name}.`), 100, 'Lee');
// return 값 브라우저환경: 숫자, Node.js 환경: 객체

// clearTimeout 함수의 인수로 전달하여 타이머 취소.
// 타이머가 취소되면 setTimeout 함수의 콜백 실행되지 않음.
cleartTimeout(timerId);

// setInterval
const count = 1;
const timerId = setInterval(() => {
  console.log(count);
  if (count++ === 5) clearInterval(timerId);
}, 1000);

41-3. 디바운스와 스로틀

  • scroll, resize, input, mousemove 와 같은 이벤트는 짧은 시간 간격으로 연속해서 발생하는데 여기 바인딩 된 이벤트 핸들러는 과도하게 호출되어 성능에 문제를 일으킬 수 있다.
  • 디바운스와 스로틀은 짧은 시간 간격으로 연속해서 발생하는 이벤트를 그룹화해 과도한 이벤트 핸들러 호출을 방지하는 기법이다.

디바운스

  • 짧은 시간 간격으로 발생하는 이벤트를 그룹화해서 마지막에 한 번만 이벤트 핸들러가 호출되도록 한다.

스로틀

  • 짧은 시간 간격으로 이벤트가 연속해서 발생하더라도 일정 시간 간격으로 이벤트 핸들러가 최대 한 번만 호출되도록 한다.
<!-- example1 -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<button class="button">click me</button>
<pre>일반 클릭 이벤트 카운터 <span class="normal-msg">0</span></pre>
<pre>debounce 클릭 이벤트 카운터 <span class="debounce-msg">0</span></pre>
<pre>throttle 클릭 이벤트 카운터 <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 timerId;
    return event => {
      if (timerId) clearTimeout(timerId);
      timerId = setTimeout(callback, delay, event);
    }
  };
  const throttle = (callback, delay) => {
    let timerId;
    return event => {
      if (timerId) return;
      timerId = setTimeout(() => {
        callback(event);
        timerId = null;
      }, delay, event);
    };
  };
  $button.addEventListener('click', () => {
    $normalMsg.textContent++;
  });
  $button.addEventListener('click', debounce(() => {
    $debounceMsg.textContent++;
  }, 500));
  $button.addEventListener('click', throttle(() => {
    $throttleMsg.textContent++;
  }, 500));
</script>
</body>
</html>
<!-- example2 -->
<!DOCTYPE html>
<html lang="en">
<head>
  <style>
      .container {
          width: 300px;
          height: 300px;
          background-color: rebeccapurple;
          overflow: scroll;
      }

      .content {
          width: 300px;
          height: 1000vh;
      }
  </style>
  <meta charset="UTF-8">
  <title>Title</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>

<input type="text">
<div class="msg"></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 event => {
      if (timerId) return;
      timerId = setTimeout(() => {
        callback(event);
        timerId = null;
      }, delay, event);
    };
  };

  let normalCount = 0;
  $container.addEventListener('scroll', () => {
    $normalCount.textContent = ++normalCount;
  });

  let throttleCount = 0;
  $container.addEventListener('scroll', throttle(() => {
    $throttleCount.textContent = ++throttleCount;
  }, 100));

  //
  const $input = document.querySelector('input');
  const $msg = document.querySelector('.msg');

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

    return event => {
      if (timerId) clearTimeout(timerId);
      timerId = setTimeout(callback, delay, event);
    }
  };

  $input.oninput = debounce(e => {
    $msg.textContent = e.target.value;
  }, 300);
</script>
</body>
</html>

' > 모던 자바스크립트 딥다이브' 카테고리의 다른 글

43장. Ajax  (0) 2022.05.22
42장. 비동기 프로그래밍  (0) 2022.05.22
46장. 제너레이터와 async/await  (0) 2022.05.15
45장. 프로미스  (3) 2022.05.14
44장. REST API  (0) 2022.05.14