[Javascript] 타이머 함수(setTimeout, setInterval)


Study/Javascript, Jquery, CSS  2020. 3. 26. 01:46

안녕하세요. 명월입니다.


이 글은 Javascript의 타이머 함수(setTimeout, setInterval)에 대한 글입니다.


자바스크립트를 작성하다보면 성능에 대한 압박을 받게 됩니다. 왜냐하면 Javascript의 처리는 서버에서 하는 작업이 아닌 클라이언트에서 하는 작업입니다.

그러므로 서버의 입장에서는 클라이언트의 모든 환경을 알 수가 없습니다. 물론 성능이 좋은 클라이언트 PC라면 문제가 없지만, 성능이 안 좋은 클라이언트 PC라면, 싱글 스레드로 많은 제어문과 함수를 실행하면 overflow가 발생할 수도 있고 브라우저가 멈출 수도 있습니다.

그럴 때는 적당히 작업을 분산 처리시키는 작업에 대한 요구가 생기게 됩니다.


Javasciprt는 기본적으로 single thread로 처리됩니다. 그래서 병렬 처리가 쉽지는 않습니다. (multi thread가 안되는 건 아니지만..)

그래서 해결할 수 있는 방법이 이벤트 타이머를 이용하는 방법입니다. 이벤트 타이머(setTimeout, setInterval)는 쓰레드 함수는 아니지만 어느 정도 작업을 분산 시킬 수 있습니다.

먼저 Javascript stack영역에서 로직이 실행이 되는 중간에 setTimeout함수를 만나게 되면 브라우저의 메시지 큐에 Timer함수를 던져 이벤트를 발생시켜 실행하게 하는 기능을 가지고 있습니다.


쉽게 이야기하면 브라우저 로드가 완료(Javascript의 실행까지 완료)가 되면 모든 요소(Element)들은 이벤트를 기다리고 있습니다. click 이벤트처럼 유저의 동작을 기다리는 이벤트도 있지만, css의 transform처럼 동적으로 랜더링하는 이벤트던가 load의 관한 이벤트등을 기다리게 됩니다.

그리고 이런 이벤트를 순차적으로 처리하기 위해서 메시지 큐라는게 존재하게 됩니다.

링크 - https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop


더 쉽게 이야기하면 Javascript의 정적인 처리가 끝나면 동적인 처리를 위한 이벤트를 대기합니다.

이 동적인 처리를 위한 이벤트에 적절한 시간을 주어서 예를 들면 1초 후에 실행 같은 시간을 설정하여 실행 처리를 분산시키면 페이지가 열릴 때의 페이지 로드 압박에서 조금은 여유가 생기게 됩니다.

<!doctype html>
<html>
 <head>
  <title>Document</title>
 </head>
 <body>
  <script>
    // 메시지 큐에 람다식 넣는다.
    // 1초 후에 람다식을 실행하라는 뜻이다.
    setTimeout(()=>{
      // 화면에 콘솔 추가.
      document.body.innerHTML += "<br /> setTimeout!!";
    },1000);
    // 0부터 99까지
    for(let i=0; i<100;i++) {
      // 콘솔 출력
      document.write(i);
      if(i%10 === 9) {
        // 줄바꿈
        document.write("<br />");
        continue;
      }
      // 띄어쓰기
      document.write(" ");
    }
  </script>
 </body>
</html>

setTimeout에 1초(1초=1000ms)후에 람다식을 넣으라는 timer를 넣었습니다.setTimeout에는 1초 후에 실행하라는 큐를 넣었지만 javascript의 실행이 끝나야 메시지 큐가 움직이기 때문에 for문에서 10초의 처리가 나오면 그 전까지는 setTimeout의 처리는 실행되지 않습니다.

즉, 1초라고 해봐야 반드시 1초 후에 실행하는 건 아니고 메시지 큐에서 1초 정도 되는 Queue의 시점에서 실행되는 것입니다.

그래서 이 setTimeout의 시간 설정은 시간상의 간격의 처리가 아니면 생략이 가능합니다.

<!doctype html>
<html>
 <head>
  <title>Document</title>
 </head>
 <body>
  <script>
    // 시간 생략을 하고 람다식만 넣었다.
    setTimeout(()=>{
      // 화면에 콘솔 추가.
      document.body.innerHTML += "시간 설정 생략!!<br />";
    });
    // element 변수
    var $span = null;
    var timer = ()=>{
      // element 변수가 null이면 추가한다.
      if($span === null){
        // element 생성
        $span = document.createElement("span");
        // element 추가
        document.body.appendChild($span);
      }
      // 현재 시간을 넣는다.
      $span.innerHTML = new Date();
      // 재귀적으로 timer함수를 1초 후에 실행하라고 메시지 큐에 넣는다.
      setTimeout(timer,1000);  
    };
    // timer함수를 1초 후에 실행하라고 메시지 큐에 넣는다.
    setTimeout(timer,1000);
  </script>
 </body>
</html>

위 예제를 보면 첫번째 setTimeout은 시간 설정을 하지 않았습니다. 그래서 load가 끝나는 시점에 거의 가장 먼저 실행되는 이벤트가 되었을 것입니다.

두번째의 경우는 1초후에 timer 함수를 실행하라는 명령을 했습니다. 그런데 timer 함수 안에 보면 또 setTimeout이 있습니다.

즉, 1초마다 setTimeout이 실행되는 함수가 되는 것입니다. 물론 이렇게 사용해도 아무 문제 없습니다만, 반복적으로 시간 차에 메시지 큐에 이벤트를 넣고 싶으면 setInterval의 함수가 있습니다.

<!doctype html>
<html>
 <head>
  <title>Document</title>
 </head>
 <body>
  <script>
    // element 생성
    var $span = document.createElement("span");
    // element 추가
    document.body.appendChild($span);
    // 1마다 실행되는 이벤트
    setInterval(()=>{
      // 현재 시간을 넣는다.
      $span.innerHTML = new Date();
    },1000);
  </script>
 </body>
</html>

여기서 이벤트 큐를 이용해서 병렬처리가 실행되는 것처럼 프로그램을 만들었습니다.

그럼 이벤트 큐에 time 이벤트를 넣어서 실행하게 했다면 반대로 실행되기 전에 이벤트를 제거할 수도 있습니다.

<!doctype html>
<html>
 <head>
  <title>Document</title>
 </head>
 <body>
  <script>
    // 시간 생략을 하고 람다식만 넣었다.
    var timeoutId = setTimeout(()=>{
      // 화면에 콘솔 추가.
      document.body.innerHTML += "생략!!<br />";
    });
    // element 생성
    var $span = document.createElement("span");
    // element 추가
    document.body.appendChild($span);
    // 1마다 실행되는 이벤트
    var intervalId = setInterval(()=>{
      // 현재 시간을 넣는다.
      $span.innerHTML = new Date();
    },1000);
    // timer이벤트 삭제
    clearTimeout(timeoutId);
    // interval이벤트 삭제
    clearInterval(intervalId);
  </script>
 </body>
</html>

위는 이벤트 큐에서 함수가 실행되기 전에 clearTimeout과 clearInterval를 통해 이벤트가 삭제되었기 때문에 실행되지 않을 것입니다.

여기서 재미있는 것은 clearTimeout와 clearInterval은 같은 함수입니다. 즉 setTimeout이나 setInterval은 같은 메시지 큐의 값을 리턴하기 때문에 setTimeoutId를 clearInterval로 삭제해도 삭제가 됩니다.

그러나 소스의 가독성을 위해 일치시키는 편이 좋을 듯 싶네요.

참조 - https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout


여기까지 Javascript의 타이머 함수(setTimeout, setInterval)에 대한 글이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.