이번에 setTimeout을 Date.now로 변경하여 해결한 경험이 있었다.
이 과정에서 setTimeout에 대해 자세히 분석하고,
어떠한 것이 문제로 이루어지는지 확인하였고,
Date.now를 활용하면 어떠한 방식으로 작동이 변화하고 해결할 수 있는지 정리하였다.
과제 내에서 setTimeout 문제가 있었다. 시간이 맞지 않았고, 일정하지 않아서 이로 인해 유저 경험을 통제할 수 없었다.
그래서 제일 의심 되는 것은 특정 동작에서 비용이 큰 함수가 콜스텍에 있거나, SMART TV 성능으로 인한 문제라고 의심하였다.
MDN 문서(setTimeout MDN)를 확인해보면 Reasons for delays longer than specified
이라는 내용이 있는데
Nested timeouts
As specified in the HTML standard, browsers will enforce a minimum timeout of 4 milliseconds once a nested call to setTimeout has been scheduled 5 times.
이중 이 문구를 의심하였지만 내가 개발하는 페이지에서는 해당 내용은 아니었다.
그래서 setTimeout 동작 방식을 확인해보았다.
1. setTimeout 호출:
setTimeout을 호출하면 이는 콜 스택에 들어가고, 지연 시간과 함께 콜백 함수는 Web API(브라우저 환경에서)로 넘어간다.
2. 타이머의 동작:
지정된 지연 시간 동안 타이머는 Web API의 관리 하에 있고 이 기간 동안 콜백 함수는 대기 상태에 있으며, 콜 스택은 다른 작업을 계속 수행한다.
3. 지연 시간 완료:
지연 시간이 완료되면, Web API는 콜백 함수를 이벤트 루프의 태스크 큐(또는 콜백 큐)로 이동한다.
4. 이벤트 루프와 콜 스택:
이벤트 루프는 콜 스택이 비어 있을 때 태스크 큐에 있는 콜백 함수를 콜 스택으로 이동시켜 실행합니다.
위 방식을 통해 setTimeout은 비동기로 작동하는 이유는 싱글스레드의 단점인 블로킹을 피하면서, 동시에 타이머 측정이 가능하게 하기 위해 만들어진 것이라고 생각하였다.
그래서 위 내용을 근거로 생각했을 때, 3가지를 고려해볼 수 있었다.
동기 함수로 설정하고, Date.now를 사용하여서 시간을 계산하는 방법으로 설계했다.
Date.now 메소드는 UTC 기준으로 1970년 1월 1일 0시 0분 0초부터 현재까지 경과된 밀리초를 반환합니다.
위 방식을 통해 경과된 시간을 비교하여서 시간 차를 구할 수 있다.
setTimeout
을 이용한 스로틀링)const THROTTLE_INTERVAL = 200;
const MyComponent = () => {
const isThrottle = useRef(false);
const handler = (e) => {
if (isThrottle.current) return;
isThrottle.current = true;
setTimeout(() => {
isThrottle.current = false;
}, THROTTLE_INTERVAL);
};
// 컴포넌트의 나머지 부분
};
setTimeout
은 THROTTLE_INTERVAL
후에 isThrottle.current
를 false
로 설정하여 다음 이벤트 핸들러 호출을 허용합니다.Date.now()
을 이용한 스로틀링)const THROTTLE_INTERVAL = 200;
const MyComponent = () => {
const lastTime = useRef(Date.now());
const handler = (e) => {
const now = Date.now();
const timeElapsed = now - lastTime.current;
if (timeElapsed < THROTTLE_INTERVAL) return;
lastTime.current = now;
};
// 기타 로직
};
lastTime
에 기록하고, 이벤트 발생 시 그 시간과 비교하여 THROTTLE_INTERVAL
이내의 호출은 무시합니다.setTimeout
방식은 비동기적으로 실행되며 지정된 지연 시간 후에 콜백이 실행됩니다. 반면, Date.now()
방식은 즉시 실행되며 동기적입니다.setTimeout
의 콜백은 Web API를 거쳐 이벤트 루프와 태스크 큐를 통해 처리됩니다. 이와 달리, Date.now()
방식은 콜 스택에서 직접 처리됩니다.개선된 로직은 이벤트 핸들링의 효율성과 응답성을 향상시키며, 기존의 버벅거림 문제를 해결하였습니다.
곧 상용화가 얼마 안남아서 추가 개발건에만 도입하고 다른 로직은 건드리지 못했다. 나중에 QA기간이 오면 다른 로직에도 도입을 검토해볼 것이다.
리펙토링한 방향은 타이머를 없앴기 때문에 비용이 줄었지만, 동기 함수로 전환하였으니 동기함수에도 영향을 줄 수 있고, 상용화가 얼마 안남아서 신중할 생각이다.
그래도 프로젝트 진행하는 동안 항상 고민이었는데, 대처하는 방법을 찾은 것 같아서 좋았다.
또한 기존 로직에서 리펙토링할 때, 현재 로직 작동하는 메커니즘에 대해 분석하고 이에 따른 해결책을 도출하는 과정이 만족스러웠다.
출처 : https://developer.mozilla.org/en-US/docs/Web/API/setTimeout