자바스크립트/모던 자바스크립트 Deep Dive

타이머

막이86 2023. 11. 20. 09:26
728x90

모던 자바스크립트 Deep Dive을 요약한 내용입니다.

41.1 호출 스케줄링

  • 함수를 명시적으로 호출하지 않고 일정 시간이 경과한 이후에 호출되도록 함수 호출을 예약하려면 타이머 함수를 사용한다.
    • 이를 호출 스케줄링이라 한다.
  • 타이머를 생성하는 함수 setTimeout과 setInterval
  • 타이머를 제거할 수 있는 함수 clearTimeout과 clearInterval
  • 타이머 함수는 생성한 타이머가 만료되면 콜백 함수가 호출 된다.
  • setTimeout 함수가 생성한 타이머는 단 한번 동작
  • setInterval 함수가 생성한 타이머는 반복 동작
  • setTimeout, setInterval은 비동기 처리 방식으로 동작

41.2 타이머 함수

41.2.1 setTimeout / clearTimeout

  • setTimeout 함수는 두번째 인수로 전달받은 시간(ms, 1/1000초)로 단한번 동작하는 타이머
  • 타이머가 만료되면 첫번째 인수로 전달받은 콜백 함수가 호출된다.매개변수 설명 
    func 타이머가 만료된 뒤 호출될 콜백 함수
    delay 타이머 만료 시간, 인수 전달을 생략한 경우 기본값이 0이 지정된다.
    param1, param2, … 호출 스케줄링된 콜백 함수에 전달해야 할 인수가 존재하는 경우 세번째 이후의 인수로 전달할 수 있다.
    setTimeout(() => console.log('Hi!'), 1000)  // Hi!
    
    setTimeout(name => console.log(`Hi! ${name}.`), 1000, 'Lee') // Hi! Lee.
    
    setTimeout(() => console.log('Hello!'))     // Hello!
    
  • const timeoutId = setTimeout(func|code[, delay, param1, param2, ...])
  • setTimeout 함수는 생성된 타이머를 식별할 수 있는 고유한 타이머 id를 반환한다.
    • 브라우저 환경의 경우 숫자
    • Node.js 환경인 경우 객체
  • clearTimeout 함수의 인수로 전달하여 타이머를 취소할 수 있다.
const timeoutId = setTimeout(func|code[, delay, param1, param2, ...])

41.2.2 setInterval / clearInterval

  • setInterval 함수는 두번째 인수로 전달받은 시간(ms, 1/1000초)으로 반복동작하는 타이머를 생성
  • 타이머가 만료될 때마다 첫번째 인수로 전달받은 콜백 함수가 반복 호출 된다.
  • 두번째 인수로 전달받은 시간이 결과할 때마다 반복 실행
  • setInterval 함수는 생성된 타이머를 식별할 수 있는 고유한 타이머 id를 반환한다.
    • 브라우저 환경의 경우 숫자
    • Node.js 환경인 경우 객체
  • clearInterval 함수의 인수로 전달하여 타이머를 취소할 수 있다.
  • let count = 1 const timeoutId = setInterval(() => { console.log(count) if (count++ === 5) { clearInterval(timeoutId) } }, 1000)

41.3 디바운스와 스로틀

  • scroll, resize, input, mousemove, mouseover 같은 이벤트는 짧은 시간 간격으로 연속해서 발생한다.
  • 이벤트 핸들러는 과도하게 호출되어 성능에 문제를 일으킬수 있다.
  • 디바운스와 스로틀은 짧은 시간 간격으로 연속해서 발생하는 이벤트를 그룹화해서 과도한 이벤트 핸들러의 호출을 방지하는 프로그래밍 기법이다.
<!DOCTYPE html>
<html>
  <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 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 = +$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>

41.3.1 디바운스

  • 디바운스는 짧은 시간 간격으로 이벤트가 연속해서 발생하면 이벤트 핸들러를 호출하지 않다가 일정시간이 경과한 이후에 이벤트 핸들러가 한번만 호출되도록 한다.
  • 마지막에 한번만 이벤트 핸들러가 호출되도록 한다.
  • input 이벤트가 짧은 시간 간격으로 연속해서 발생하는 경우
<!DOCTYPE html>
<html>
  <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) => {
          if (timerId) {
            clearTimeout(timerId)
          }
          timerId = setTimeout(callback, delay, event)
        }
      }

      $input.oninput = debounce((e) => {
        $msg.textContent = e.target.value
      }, 300)
    </script>
  </body>
</html>
  • 스로틀은 짧은 시간 간격으로 이벤트가 연속해서 발생하더라도 일정 시간 간격으로 이벤트 핸들러가 최대 한번만 호출되도록 한다.
    • scroll 이벤트가 짧은 시간 간격으로 연속해서 발생하는 경우
<!DOCTYPE html>
<html>
  <head>
    <style>
      .container {
        width: 300px;
        height: 300px;
        background-color: 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) => {
          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)
      )
    </script>
  </body>
</html>
  • 41.3.2 스로틀
  • throttle 함수는 Underscore의 throttle 함수나 Lodash의 throttle 함수는 사용하는 것을 권장
728x90

'자바스크립트 > 모던 자바스크립트 Deep Dive' 카테고리의 다른 글

Ajax  (0) 2023.11.20
비동기 프로그래밍  (0) 2023.11.20
이벤트  (2) 2023.11.20
브라우저의 렌더링 과정  (1) 2023.11.17
Set과 Map  (0) 2023.11.17