드림오구
article thumbnail

🌊 타이머

: 시간이 경과된 이후에 호출되도록 함수 호출을 예약하려면 타이머 함수를 사용한다. 타이머 함수는 브라우저 환경과 Node.js 환경에서 모두 전역 객체의 메서드로서 제공하는 함수이다. 

 

타이머 생성 함수 : 일정 시간이 경과된 이후 콜백 함수를 호출, 비동기 처리 방식으로 동작한다.

  • setTimeout : 단 한 번 동작한다.
  • setInterval : 반복해서 동작한다.

타이머 제거 함수 

  • clearTimeout 
  • clearInterval

 

타이머 함수

setTimeout / clearTimeout

const timeout = setTimeout(func|code[, delay, param1, param2])
  • func : 타이머가 만료된 뒤 호출될 콜백함수
  • delay : 타이머 만료 시간은 밀리초(ms) 단위이며 생략시 0이 전달된다. → 시간이 만료됐다고 콜백함수가 즉시 호출되는 것이 아닌, 태스크 큐에 등록하는 시간을 지연시킨다. 
  • param1, param2 : 콜백함수에 전달해야할 인수가 존재하는 경우, 세 번째 이후의 인수로 전달할 수 있다. (IE9 이하는 전달 불가)
// 1초 뒤 타이머가 만료되면 콜백함수를 호출 한다.
setTimeout(() => console.log('Shoupeach babo!'), 1000);

// chunsik이 콜백함수의 인수로 전달된다. 
setTimeout(name => console.log(`${name} babo!`), 1000, 'chunsik')'

 

setTimeout 함수는 생성된 타이머를 식별할 수 있는 고유한 타이머 id를 반환한다.

 

타이머 id

  • 브라우저 : 숫자
  • Node.js : 객체
console.log(setTimeout(() => console.log('ogu'), 1000));

// 브라우저 : 5350 반환
// node.js : 객체 반환
// Timeout {
//   _idleTimeout: 1000,
//   _idlePrev: [TimersList],
//   _idleNext: [TimersList],
//   _idleStart: 18,
//   _onTimeout: [Function (anonymous)],
//   _timerArgs: undefined,
//   _repeat: null,
//   _destroyed: false,
//   [Symbol(refed)]: true,
//   [Symbol(kHasPrimitive)]: false,
//   [Symbol(asyncId)]: 5,
//   [Symbol(triggerId)]: 1
// }

 

clearTimeout(타이머함수의 id, 또는 변수명)으로 호출을 취소할 수 있다.

const timeFnc = setTimeout(()=> console.log('ogu!'), 10000);

clearTimeout(timeFnc)
// 변수명으로도 setTimeout을 clear할 수 있다.

 

 

setInterval / clearInterval

두 번째 인수로 전달받은 시간으로 반복 동장하는 타이머를 생성한다. 타이머가 만료 될 때 마다, 첫 번째 인수로 전달 받은 콜백 함수가 반복 호출된다. 

 

const timerId = setInterval(func|code[, delay, param1, param2, ...]);

 

let count = 1;
const timeoutId = setInterval(() => {
  console.log(count);
  // count++를 했을 때 5가 될 경우 timeoutId를 clear한다.
  if (count++ === 5) clearInterval(timeoutId);
});

 

 

디바운스와 스로틀

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

 

 

디바운스

: 짧은 시간 간격으로 이벤트가 연속으로 발생하면 이벤트 핸들러를 호출하지 않다가, 일정 시간이 경과한 이후 이벤트 핸들러를 한 번 호출. 

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

 

  <body>
    <input type="text" />
    <div class="msg"></div>
    <script>
      const input = document.querySelector('input');
      const msg = document.querySelector('.msg');

      const debounce = (callback, delay) => {
        let timerId;
        return (event) => {
          // delay 경과 전 이벤트가 발생하면 이전 타이머는 취소하고 새로운 타이머를 재설정한다.
          if (timerId) clearTimeout(timerId);
          timerId = setTimeout(callback, delay, event);
        };
      };
      input.oninput = debounce((e) => {
        msg.textContent = e.target.value;
      }, 300);
    </script>
  </body>

input 이벤트 핸들러에서 사용자가 입력 필드에 입력한 값으로 Ajax 요청과 같은 무거운 처리를 수행한다면 사용자가 아직 입력을 완료하지 않았어도 Ajax 요청이 전송되는 것이 반복되어 서버에 부담을 주게 된다. 사용자가 입력 완료 했을 때 한 번만 Ajax 요청을 전송하는게 바람직하다. 

 

사용 예

  • 디바이스 resize 이벤트
  • input 요소에 입력된 값으로 ajax 요청하는 입력 필드 자동완성 UI 구현
  • 버튼 중복 클릭 방지 처리

 

실무 Tip
위에서 구현한 debounce 함수는 간략하게 구현되어 있기 때문에 Underscore의 debounce 함수Lodash의 debounce 함수를 사용하는 것을 권장한다.

 

 

스로틀

: 짧은 시간 간격으로 이벤트가 연속해서 발생하더라도 일정 시간 간격으로 이벤트 핸들러가 최대 한 번만 호출되도록 한다. 즉, 짧은 시간 간격으로 연속해서 발생하는 이벤트를 그룹화하여 일정 시간 단위로 이벤트 핸들러가 호출되도록 호출 주기를 만든다.

<!DOCTYPE html>
<html lang="ko">
  <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>throttle-count</title>
    <style>
      .container {
        width: 300px;
        height: 300px;
        background: rebeccapurple;
        overflow: scroll;
      }
      .content {
        width: 300px;
        height: 1000vh;
      }
    </style>
  </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 (event) => {

          // delay 간격으로 콜백함수가 호출된다.

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

      let normalCount = 0;
      // throttle 함수가 반환하는 클로저가 이벤트 핸들러로 등록된다. 
      $container.addEventListener('scroll', () => {
        $normalCount.textContent = ++normalCount;
      });
      let throttleCount = 0;
      $container.addEventListener(
        'scroll',
        throttle(() => {
          $throttleCount.textContent = ++throttleCount;
        }, 100)
      );

    </script>
  </body>
</html>

 

해당 코드 스크롤 하였을 때 이벤트 처리 횟수 비교 사진

이처럼 짧은 시간 간격으로 연속해서 발생하는 이벤트의 과도한 이벤트 핸들러 호출을 방지하기 위해 throttle 함수는 이벤트를 그룹화해서 일정 시간 단위로 이벤트 핸들러가 호출되도록 호출 주기를 만든다. throttle 함수는 두 번째 인수인 delay 시간 간격으로 콜백 함수를 호출한다.

 

사용 예

  • scroll 이벤트 처리
  • 무한 스크롤 UI 구현 
  •  
실무 Tip
위에서 구현한 debounce 함수는 간략하게 구현되어 있기 때문에 Underscore의 throttle 함수 Lodash의 throttle 함수를 사용하는 것을 권장한다.

 

 

 

'🐣 STUDY > Java Script' 카테고리의 다른 글

🌊 [JAVASCRIPT] 비동기 프로그래밍  (2) 2023.04.19
🌊 [JAVASCRIPT] AJAX  (2) 2023.04.12
🌊 [JAVASCRIPT] 재귀함수  (2) 2023.04.11
🌊 [javascript] 생성자 함수  (2) 2023.03.29
🌊 [javascript] 프로토타입 체인  (2) 2023.03.16
profile

드림오구

@드림오구