Javascript에서 함수를 명시적으로 호출하면 함수가 즉시 실행된다. 함수 호출 시점을 임의적으로 조정하기 위해서는 타이머 함수를 사용해야 한다. 이를 호출 스케줄링(schedule a call)이라 한다.
Javascrip에서 사용할 수 있는 타이머 함수는 setTimeout과 setInterval이 있다. 이 타이머를 제거할 수 있는 clearTimeout와 clearInterval을 제공한다. 사실 타이머 함수들은 ECMAScript에 정의된 빌트인 함수가 아니다. Node.js와 브라우저 환경에서 제공하는 함수다.
setTimeout과 setInterval 모두 타이머 함수로 일정 시간이 경과된 후 콜백함수를 호출한다. 가장 큰 차이점은 setTimeout이 매개변수로 주어진 시간 후 딱 한번만 콜백함수를 호출하는 반면, setInterval은 매개변수로 주어진 시간 간격으로 무한히 반복 호출한다.
타이머 함수는 기본적으로 비동기적으로 작동한다. 하지만 Javascript는 단 하나의 실행 컨텍스트 스택을 가지기 때문에 두개 이상의 태스크를 동시에 실행할 수 없다. Javascript 엔진은 싱글 스레드(Single Thread)로 작동하기 때문이다.
setTimeout()
setTimeout 함수는 두번째 인자로 전달받은 시간 변수(ms 단위, 1000이면 1초) 후 단 한번만 동작하는 타이머를 생성한다. 타이머 만료 후 첫번째 인자로 전달받은 콜백함수를 호출한다.
setTimeout(func|code[, delay, param1, param2, ...]);
- func : 타이머 만료 후 호출될 콜백함수
- delay : 타이머가 지연될 시간이다. 타이머를 지정한다고 해서 무조건 콜백함수 호출의 지연을 보장하지 않는다. delay 값은 단지 태스크 큐에 콜백 함수를 등록하는 시간을 지연할 뿐이다. 만약 콜 스택이 채워져 있다면 지연시간은 더 걸릴 수도 있다는 말이다. 만약 delay 값이 주어지지 않으면 0으로 초기화 된다.
- param1, 2 : 콜백 함수에 전달해야할 인수가 존재하는 경우 매개변수로 던질 수 있다.
setTimeout 함수는 고유한 타이머 id 값을 반환한다. 브라우저 console 창에서 setTimeout 함수를 실행하면 숫자가 반환되는데, 이게 타이머 id다. Node.js 환경에서 실행하면 객체가 반환되는데, 이게 타이머 id다.
브라우저 환경
브라우저 환경에서 setTimeout 함수를 실행하면 고유한 타이머 id를 반환한다. 63이 타이머 id다.
Node.js 환경
Node.js 환경에서 setTimeout 함수를 실행하면 타이머 id 객체가 반환된다.
Timeout {
_idleTimeout: 1000,
_idlePrev: [TimersList],
_idleNext: [TimersList],
_idleStart: 44,
_onTimeout: [Function (anonymous)],
_timerArgs: undefined,
_repeat: null,
_destroyed: false,
[Symbol(refed)]: true,
[Symbol(kHasPrimitive)]: false,
[Symbol(asyncId)]: 2,
[Symbol(triggerId)]: 1
}
// 타이머 함수에 전달할 콜백 함수
function test(){
console.log("hello world");
}
setTimeout(test, 1000);
hello world
타이머 함수에 매개변수를 보낼 수 있다.
// 매개변수를 전달받는 콜백함수
function test(val){
console.log("hello world", val);
}
setTimeout(test, 1000, "About-Tech");
hello world About-Tech
clearTimeout
setTimeout의 고유한 타이머 id값을 사용해서 타이머를 취소할 수 있다. 호출 스케줄링을 취소하는 함수는 clearTimeout이다.
아래 예시에서 setTimeout 함수가 실행되면서 호출 스케줄링이 생성된다. 타이머 id는 68번이다. 타이머 id를 매개변수로 던져서 clearTimeout() 함수를 실행하면 호출 스케줄링이 취소된다.
setInterval()
setInterval 함수는 두번째 인자로 전달받은 시간 간격으로 동작하는 타이머를 생성한다. 첫번째로 전달받는 콜백함수가 시간 간격을 두고 반복적으로 호출된다.
setInterval(func|code[, delay, param1, param2, ...]);
setInterval 함수는 고유한 타이머 id를 반환한다. 브라우저 환경에서는 숫자로, Node.js 환경에서는 객체로 반환된다.
브라우저 환경
setInterval(()=>{console.log("hello world")}, 1000);
69
Node.js 환경
const timerId = setInterval(()=>{console.log("hello world")}, 1000);
console.log(timerId);
Timeout {
_idleTimeout: 1000,
_idlePrev: [TimersList],
_idleNext: [TimersList],
_idleStart: 59,
_onTimeout: [Function (anonymous)],
_timerArgs: undefined,
_repeat: 1000,
_destroyed: false,
[Symbol(refed)]: true,
[Symbol(kHasPrimitive)]: false,
[Symbol(asyncId)]: 2,
[Symbol(triggerId)]: 1
}
clearInterval
setInterval 함수로 생성된 타이머를 취소하기 위해서는 타이머의 고유한 id 값을 이용한다. clearInterval 함수에 타이머 id를 매개변수로 보내서 타이머를 취소할 수 있다.
예제 소스코드
1초 간격으로 콜백 함수를 실행하다가 count가 10이상이 되면 clearInterval() 함수를 실행해서 타이머를 삭제한다.
let count = 0;
const timerId = setInterval(()=>{
console.log(`COUNTER : ${count++}`);
if(count >= 10){
clearInterval(timerId);
}
}, 1000)
타이머 함수 실제 예제
짧은 시간내에 동일한 이벤트가 다수 발생했을 때 이벤트 핸들러를 실행할 경우 프로그램의 성능은 저하될 수 밖에 없다. 예를 들어 수강 신청 같은 분초를 다투는 환경에서 사용자는 "등록" 버튼을 초당 수십번씩 클릭하게 된다.
만약 짧은 시간내 동일한 이벤트에 대한 핸들러를 모두 처리한다면 서버의 성능은 과부화를 겪을 수 밖에 없다. 이를 방지하기 위해서 이벤트들을 그룹화 해서 과도한 이벤트 핸들러 호출을 방지하는 것이 디바운스(Debounce)와 스로틀(Throttle) 이다.
짧은
디바운스(Debounce)
디바운스는 짧은 시간 간격으로 이벤트가 연속해서 발생하는 경우 이벤트 핸들러를 호출하지 않다가 일정 시간 후 이벤트를 단 한번만 호출한다.
코드를 살펴보면, 만약 setTimeout함수가 호출되어 timerId가 생성되었다면 타이머 객체를 삭제한다. 이 후 다시 타이머 객체를 생성한 후 타이머 Id값을 할당한다.
디바운스 함수는 클로저를 사용해서 timerId 값을 기억한다.
const debounce = (callback, delay) =>{
let timerId;
return event => {
if(timerId) clearTimeout(timerId);
timerId = setTimeout(callbak, delay, event);
}
}
input 컴포넌트에서 사용자가 연속적인 이벤트를 짧은 시간안에 실행시키면 무거운 Ajax 요청이 반복되면서 서버에 과부화가 걸리게 된다. 이를 방지하기 위해서 resize 이벤트나 input 이벤트에서 입력 필드 자동완성 UI 구현, 버튼 중복 클릭 방지 UI 등에 디바운스 함수가 사용된다.
스로틀(Throttle)
스로틀 함수는 짧은 시간 간격으로 이벤트가 연속해서 발생하는 경우 일정 시간 간격으로 이벤트가 발생하도록 호출 간격을 조정한다.
delay가 경과하기 전 이벤트가 발생하면 아무것도 하지 않는다. delay가 경과한 후 이벤트가 발생하면 새로운 타이머를 생성한다. 결과적으로 delay 간격으로 이벤트가 호출된다.
const throttle = (callback, delay) => {
let timerId;
return event => {
if(timerId) return;
timerId = setTimeout(()=>{
callback(event);
timerId = null;
}, delay, event);
}
}
이벤트들을 그룹화하여 일정 시간간격으로 이벤트 핸들러를 호출하는 스로틀 함수는 scoll 처리 혹은 무한 스크롤(Infinite Scroll) UI 구현에 사용된다.
'Programming' 카테고리의 다른 글
[Programming] CORS란? (preflight, OPTIONS 메소드?) (0) | 2022.05.25 |
---|---|
[React] Effect HOOK API useEffect 사용하는 방법 (0) | 2022.05.25 |
[Javascript] apply() vs call() vs bind() 차이 (0) | 2022.05.21 |
[Javascript] AJAX란? (feat. Fetch, XHR, 장점 단점) (0) | 2022.05.20 |
[Network] HTTP Message Request Response 동작 이해하기 (0) | 2022.05.20 |
댓글