해당 포스팅은 위키북스의 **모던 자바스크립트 Deep Dive**라는 책을 독학하며 기록하는 글입니다.

함수를 명시적으로 호출하면 함수가 즉시 실행되는데, 이 함수를 일정 시간이 경과된 이후에 호출되도록 함수 호출을 예약하려면 타이머 함수를 사용하고, 이를 호출 스케줄링이라 한다.

타이머 함수

타이머 함수에는 일정시간 이후 지정된 함수를 실행시키는 setTimeout 메서드와 일정시간마다 지정된 함수를 반복 실행시키는 setInterval 메서드가 있다.

setTimeout / clearTimeout

setTimeout 메서드의 사용법은 다음과 같다.

setTimeout(콜백함수|코드, 시간[, 인자1, 인자2, ...]);

먼저 첫 번째 인자로 일정 시간 이후 호출할 함수를 콜백함수로 등록한다. 이때 함수가 미리 정의되어 있다면 해당 함수의 함수명을, 아니라면 인자로 함수 자체를 코드로 작성해도 된다.

두 번째 인자로 얼마만큼의 시간이 지난 후에 콜백함수를 호출할 것인지를 ms단위로 작성한다. 이때 아무런 값을 주지 않으면 최소시간은 4ms가 지정이 되고, 4ms보다 작은 시간을 등록해도 4ms가 등록이 된다.

나머지는 선택적인 인자로 만약 지정한 콜백함수가 인수를 전달받아야 하는 함수라면 해당 인수들을 시간 다음에 차례대로 전달할 수 있다.

만약 해당 타이머를 삭제하고 싶다면 setTimeout 메서드를 사용해 타이머를 등록할 때 반환되는 값을 저장하고 있다가 clearTimeout 메서드를 호출하면서 해당 값을 인수로 전달해주면 된다. 다음을 보자.

const timer = setTimeout(() => { console.log("Hi") }, 5000);

// 5000ms가 지나기전에
clearTimeout(timer);

setInterval / clearInterval

setInterval 메서드의 사용법은 다음과 같다.

setInterval(콜백함수|코드, 시간[, 인자1, 인자2, ...]);

먼저 첫 번째 인자로 일정 시간 이후 호출할 함수를 콜백함수로 등록한다. 이때 함수가 미리 정의되어 있다면 해당 함수의 함수명을, 아니라면 인자로 함수 자체를 코드로 작성해도 된다.

두 번째 인자로 얼마만큼의 시간마다 콜백함수를 호출할 것인지를 ms단위로 작성한다. 이때 아무런 값을 주지 않으면 최소시간은 4ms가 지정이 되고, 4ms보다 작은 시간을 등록해도 4ms가 등록이 된다.

나머지는 선택적인 인자로 만약 지정한 콜백함수가 인수를 전달받아야 하는 함수라면 해당 인수들을 시간 다음에 차례대로 전달할 수 있다.

만약 해당 타이머를 삭제하고 싶다면 setInterval 메서드를 사용해 타이머를 등록할 때 반환되는 값을 저장하고 있다가 clearInterval 메서드를 호출하면서 해당 값을 인수로 전달해주면 된다. 다음을 보자.

const timer = setInterval(() => { console.log("Hi") }, 5000);

// 5000ms가 지나기전에
clearInterval(timer);

디바운스와 스로틀

scrollresize, input 같은 이벤트는 짧은 시간을 간격으로 연속으로 발생한다. 이러한 이벤트에 바인딩한 이벤트 핸들러는 과도하게 호출되어 성능에 문제를 일으킬 수 있는데, 이를 방지할 수 있는 프로그래밍 기법은 바로 디바운스스로틀이다. 둘은 목적은 같으나, 동작하는 방식이 조금 다른데 하나씩 살펴보자.

디바운스

디바운스는 짧은 시간 간격으로 이벤트가 연속해서 발생하면 이벤트 핸들러를 호출하지 않다가 일정 시간이 경과한 이후에 이벤트 핸들러가 한 번만 호출되도록 한다.

즉, 클릭 이벤트를 등록하고 해당 요소를 100번 짧을 시간 간격으로 연속 클릭했다면 클릭 이벤트로 등록한 이벤트 핸들러가 100번 일어나는 것이 아니라 마지막에 누른 한 번에만 실행된다는 얘기다.

const debounce = (callback, delay) => {
  let timeId;
  
  // debounce는 timeId를 기억하는 클로저를 반환한다.
  return event => {
    // 이때 timeId가 있다면(짧은 시간내에 이미 이벤트가 일어났다면) 삭제
    if(timeId) clearTimeout(timeId);
    
    // 다시 delay를 기준으로 setTimeout 메서드를 등록
    // 즉, delay만큼 이벤트가 안 들어오면 콜백함수가 정상적으로 실행
    timeId = setTimeout(callback, delay, event);
}

// DOM요소에 해당 이벤트 타입이 일어날 때, 해당 이벤트가 300ms보다 짧은 간격으로 들어오면
// 무시하고 마지막에 들어온 이벤트에 대해서만 처리하는 디바운스 사용
DOM요소.on이벤트타입 = debounce(콜백함수, 300);

스로틀

스로틀은 디바운스와는 다르게 맨 처음 발생한 이벤트에 대해서 타이머를 등록하고, 일정 시간동안 짧은 시간 간격으로 연속해서 발생한 이벤트를 무시하는 알고리즘을 가지고 있다.

즉, 클릭 이벤트를 이벤트 핸들러가 5번 실행될 만큼의 시간으로 스로틀을 만들고 등록했다면 해당 요소를 100번 짧을 시간 간격으로 연속 클릭했을때 클릭 이벤트로 등록한 이벤트 핸들러가 100번 일어나는 것이 아니라 20번만 실행된다는 얘기다.

const throttle = (callback, delay) => {
  let timeId;
  
  // throttle timeId를 기억하는 클로저를 반환한다.
  return event => {
    // 이때 timeId가 있다면(짧은 시간내에 이미 이벤트가 일어났다면) 무시
    if(timeId) return;
    
    // timeId가 없다면(짧은 시간내에 이벤트가 일어난게 아니라면) dealy를 기준으로 setTimeout 메서드를 등록
    // 이때 콜백함수를 실행시키고 timeId값을 null로 바꾸는 문도 추가해줘야 한다.
    timeId = setTimeout(() => {
      callback(event);
      timeId = null;
    }, delay, event);
}

// DOM요소에 해당 이벤트 타입이 일어날 때, 해당 이벤트가 300ms보다 짧은 간격으로 들어오면
// 맨 처음에 들어온 이벤트에 대해서 처리하고 300ms동안은 무시하고 301ms부터 들어온 이벤트에 
// 대해 다시 처리하는 스로틀 사용
DOM요소.on이벤트타입 = debounce(콜백함수, 300);

하지만 책에는 책에서 사용한 디바운스와 스로틀에 대한 예제는 불완전하니 Undersore의 디바운스, 스로틀 함수나, Lodash의 디바운스, 스로틀 함수를 사용하는 것을 권장하고 있다.
따라서 본 장에서는 디바운스와 스로틀이 무엇인지와 어떤 원리인지를 숙지하도록 하자.

profile
I Will be Relaxed Person

0개의 댓글