코딩/Javascript

[JS-기초편] 7.타이머(Timers)

드리프트 2020. 12. 15. 22:47
728x170

 

 

기본적으로 코드는 동기적으로 실행됩니다.

 

동기적이라는 뜻은 명령문을 실행해야 할 때 즉시 실행한다는 뜻입니다.

 

실행을 지연시키거나 작업을 나중으로 지연시키는 개념은 JavaScript의 기본 동작이 아니며 자바스크립트 루프 편 공부할 때 잠깐 겪었을 겁니다.

 

루프는 각 사이클을 지연 없이 번개처럼 빠른 속도로 실행합니다.

 

이는 빠른 계산에 적합하지만 우리가 어떤 경우 좀 더 느린 속도로 무언가를 업데이트하려는 경우에는 좋지 않습니다.

 

즉시 실행되고 있는 작업을 중지 할 수 있는 기능이 존재하지 않는다는 것은 아닙니다.

 

그에 해당하는 기능은 setTimeout, setInterval 그리고 requestAnimationFrame의 세 가지가 있습니다.

 

이 기능에 대해 좀 더 알아봅시다.

 

 

 

setTimeout으로 지연시키기

 

setTimeout 함수를 사용하면 일부 코드 실행을 지연시킬 수 있습니다.

 

또한 사용 방식도 좋습니다.

 

setTimeout 함수를 사용하면 실행할 코드와 지정한 코드가 실행되기 전에 대기 할 시간 (밀리 초까지)을 지정할 수 있습니다.

 

이제 그 예제를 살펴보자면 JavaScript에서는 다음과 같이 것입니다.

 

let timeoutID = setTimeout(someFunction, delayInMilliseconds);

 

좀 더 예를 들어 보면, 5 초 후에 showAlert라는 함수를 호출하려는 경우 setTimeout 선언은 다음과 같습니다.

 

function showAlert() {
  alert("moo!");
}

let timeoutID = setTimeout(showAlert, 5000);

 

쉽죠? 잠시 setTimeout 공부를 위해 재미없는 얘기를 하자면,  setTimeout 함수로 초기화 된 timeoutID 변수에 대해 생각해 봐야 합니다.

 

timeoutID를 그냥 만들지는 않았습니다.

 

만약 우리가 setTimeout 타이머에 다시 액세스하려면 이를 참조 할 방법이 필요합니다.

 

그때를 위해서 이렇게 timeoutID 변수를 setTimeout과 함께 선언하면 쉽게 참조 할 수 있습니다.

 

이제 타이머를 만든 후에 왜 다시 참조할 필요가 있는지 궁금 할 것입니다.

 

그다지 많은 이유가 없습니다.

 

생각할 수 있는 유일한 이유는 타이머를 취소할때 참조 하는 것입니다.

 

setTimeout의 경우 clearTimeout 함수를 사용하고 timeoutID를 인수로 전달하면 쉽게 취소할 수 있습니다.

 

clearTimeout(timeoutID);

 

타이머를 취소 할 계획이 없는 경우 변수 선언 없이 setTimeout을 직접 사용할 수 있습니다.

 

어쨌든 setTimeout에 대한 기술적인 세부 사항은 차치하고 setTimeout이 실제 세계에서 언제 사용되는지 알아봅시다.

 

주된 이유는 UI 개발 할때입니다.

 

UI 개발을 할 때 일부 작업을 나중으로 연기하는 것은 일반적입니다.

 

 

몇가지 예를 들면,

  • 유저가 메뉴가 선택하고, 잠시후 더 이상 메뉴를 사용하지 않을 때 몇 초 후에 메뉴가 사라집니다.

  • 완료 할 수없는 장기 실행 작업이 있으며 setTimeout 함수가 해당 작업을 중단하여 제어를 사용자에게 반환합니다.

  • 사용자가 비활성 상태인지 유휴 상태인지를 감지하기 위해 setTimeout 함수를 사용하는 것입니다.

Google에서 setTimeout을 검색하면 setTimeout이 매우 유용하다는 것을 알 수 있을겁니다.

 

 

 

setInterval을 이용한 루프

 

우리가 살펴볼 다음 타이머 함수는 setInterval입니다.

 

setInterval 함수는 지정된 시간 후에 코드를 실행할 수 있다는 점에서 setTimeout과 유사합니다.

 

다른 점은 코드를 한 번만 실행하지 않는다는 점입니다. 대신 루프에서 코드를 계속 실행할 뿐입니다.

 

setInterval 함수를 사용하는 방법은 다음과 같습니다.

 

let intervalID = setInterval(someFunction, delayInMilliseconds);

 

함수 이름을 제외하고 setInterval을 사용하는 방법은 setTimeout과 동일합니다.

 

첫 번째 인수는 실행하려는 인라인 코드 또는 함수를 지정합니다.

 

두 번째 인수는 코드가 다시 반복 될 때까지 대기하는 시간을 지정합니다.

 

또한 setInterval 함수를 변수로 초기화하여 intervalID (루핑 취소할 때 사용하는 ID)를 저장할 수 있습니다.

 

이제 실제 코드를 보겠습니다.

 

다음은 각 루프 사이에 2 초의 지연 시간을 두고 drawText라는 함수를 반복하는 코드입니다.

 

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>Show me some text!</title>
</head>

<body>
  <script>
    let thingToPrint = "";
    
    function drawText() {
      thingToPrint += "#";
      document.writeln(thingToPrint);
    }

    setInterval(drawText, 2000);
  </script>
</body>

</html>

 

루핑을 취소하려면 clearInterval 함수를 사용할 수 있습니다.

 

clearInterval(intervalID);

 

사용법은 clearTimeout 함수와 유사합니다.

 

처음에 setInterval을 설정한 후 그 인스턴스를 변수로 저장했을 때 그 변수를 전달합니다.

 

실제로 setInterval은 JavaScript로 애니메이션을 만드는 데 가장 오랫동안 사용했던 주요 기능이었습니다.

 

초당 30 또는 60 프레임을 얻으려면 지연 시간 값으로 재생하여 결과를 얻을 수 있습니다.

 

// 1000 divided 60 is the millisecond value for 60fps
setInterval(moveCircles, 1000 / 60);

 

requestAnimationFrame으로 부드럽게 애니메이션하기

 

가장 유용한 함수 중 하나 인 requestAnimationFrame을 알아봅시다.

 

requestAnimationFrame 함수는 프로그래머의 코드를 브라우저 리페인트 이벤트와 동기화 시켜줍니다.

 

즉, 브라우저는 주어진 시간에 수십억 가지를 처리 하느라 바쁘다는 것입니다.

 

예를 들어 레이아웃 조작, 페이지 스크롤, 마우스 클릭 이벤트 처리, 키보드 탭 결과 표시, JavaScript 실행, 리소스 로드 등 브라우저는 많은 처리를 하느라 바쁘다는 겁니다.

 

브라우저가 이 모든 작업을 수행하는 동시에 최선을 다해 초당 60 프레임으로 화면을 다시 그리고 있는 겁니다.

 

당신이 만약 화면에 무언가를 애니메이션으로 표시하는 코드가 있는 경우, 브라우저가 수행하는 다른 작업을 방해하지 않고 애니메이션 코드가 제대로 실행되기를 바랍니다.

 

앞서 언급 한 setInterval 기술을 사용한다고 해서 브라우저가 다른 작업을 최적화 하느라 바쁠 때 프레임이 떨이지지 않는 것은 아닙니다.

 

애니메이션 코드가 다른 일반 JavaScript처럼 취급되지 않도록 하려면 requestAnimationFrame 함수를 써야합니다.

 

이 기능은 브라우저에서 특별 처리됩니다.

 

이 특별 처리를 통해 프레임 손실을 방지하고 불필요한 작업을 방지하며 일반적으로 다른 루핑 솔루션을 괴롭히는 부작용을 제거할 수 있습니다.

 

이 함수를 사용하는 방법은 setTimeout 및 setInterval과 비슷하게 시작됩니다.

 

let requestID = requestAnimationFrame(someFunction);

 

유일한 차이점은 지속 시간 값을 지정하지 않는다는 것입니다.

 

지속 시간은 현재의 프레임 속도에 따라 자동 계산됩니다.

 

현재의 프레임 속도는 현재 탭이 활성화되어 있는지, 장치가 배터리로 실행 중인지에 따라 달라집니다.

 

어쨌든 requestAnimationFrame 함수의 실제 사용은 requestAnimationFrame을 한 번만 호출하는 경우가 거의 없습니다.

 

JavaScript에서 생성된 모든 애니메이션의 핵심은 애니메이션 루프이며 requestAnimationFrame을 적용하고싶은 것이 바로 이 루프입니다.

 

그 적용의 결과는 다음과 같습니다.

 

function animationLoop() {
  // animation-related code

  requestAnimationFrame(animationLoop)
}

// start off our animation loop!
animationLoop();

 

requestAnimationFrame은 animationLoop 함수 자체 내에서 전적으로 animationLoop 함수를 호출합니다.

 

이 형태는 버그가 아닙니다.

 

이러한 종류의 순환 참조는 브라우저를 정지 또는 중지시키겠지만, requestAnimationFrame의 구현은 이를 방지합니다.

 

대신, 애니메이션 루프 함수가 매끄럽고 유동적인 애니메이션을 만들기 위해 화면에 그려지는 데 필요한 적절한 횟수만큼 호출되도록합니다.

 

나머지 애플리케이션 기능을 동결하지 않고 그렇게 한다는 겁니다.

 

requestAnimationFrame과 멋진 애니메이션 제작의 주요 용도에 대해 자세히 알아 보려면 JavaScript의 애니메이션 섹션에서 모든 콘텐츠를 살펴 볼 수 있습니다.

 

 

결론

아마도 여러분은 타이머 관련 함수가 if / else 문 그리고 루프와 같은 다른 필수 항목에 비해 더 틈새 범주에 속한다고 생각할 겁니다.

 

setTimeout, setInterval 또는 requestAnimationFrame에 의존하지 않고도 멋진 앱을 많이 만들 수 있습니다.

 

그렇다고 타이머 함수를 꼭 알아야 하는 것은 아닙니다.

 

코드가 실행될 때를 지연하거나, 코드를 계속 반복하거나, JavaScript를 사용하여 멋진 애니메이션을 만들어야 할 때가 있습니다.

 

그 시간이 되면 아마 왜 타이머 함수를 써야 하는지 알게 될 것입니다.

 

다음편에서는 변수의 유효 범위에 대해 알아 보겠습니다.

그리드형