내일배움캠프

230524 동기와 비동기 반복문 안에서 html 요소 변경

Neda 2023. 5. 24. 16:43

230524 동기와 비동기

동기적 실행

자바스크립트 문법반 4주차에서는 콜백함수와 관련해 동기와 비동기에 대해 배웠다.

대표적인 비동기 함수로 타이머 역할을 하는 setTimeout()과 setInterval()이 있었다.

일반적인 자바스크립트 코드는 동기적 실행이다.

동기적이라는 말은 코드의 한줄을 작업이 완료하길 기다렸다가 완료되면 다음줄을 실행한다.

반복문 안에서는 왜?

하지만 반복문 안에서 반복마다 html element를 변경하도록 코드를 짠 경우

for (let i = 0; i < n; i++) {
    document.getElementById("display").value = i;
}

반복문이 다 끝난 후에 바뀐다는 것이다.

단순하게 생각하면 각 반복마다 html element가 바뀌어야 할 것 같은데, 끝난 후에 바뀐다.

(물론 console.log로는 찍힌다)

결론부터 말하면

이는 자바스크립트 이벤트 루프 특성에 의한 것이라고 한다.

 

비동기 함수를 사용한 반복문

하지만 비동기 함수를 사용하면 반복문 안에서도 강제로 html 요소에 대해 변경이 가능하지 않을까 궁금했다.

따라서 시간에 따라 비동기적으로 실행되는 setInterval(),  setTimeout()과

브라우저의 렌더링 주기에 따라 실행되는 requestAnimationFrame()를 사용해 보았다.

setInterval()은 제너레이터를 통해 반복 했다.

 

실행 코드

using setInterval
0%
Duration of time:

using requestAnimationFrame
0%
Duration of time:

using setTimeout
0%
Duration of time:

 

//setInterval
function onClickStart() {
    const generator = sumFromZero(number.value);
    const start = window.performance.now();
    const setIntervalId = setInterval(() => {
        const { done, value } = generator.next();
        if (done) {
            clearInterval(setIntervalId);
            const end = window.performance.now();
            time.innerText = `${end - start} ms`;
        }
    }, 0);
}
//requestAnimationFrame
  function onClickStart2() {
    let i = 0;
    const n = number.value;
    const start = window.performance.now();
    function update() {
      const result = Math.floor((i / n) * 100);
      progress2.value = result;
      display_progress2.innerText = result;
      if (i < n) {
        i++;
        requestAnimationFrame(updateProgress);
      } else {
        const end = window.performance.now();
        time2.innerText = `${end - start} ms`;
      }
    }
    update();
  }
//setTimeout
  function onClickStart3() {
    const n = number.value;
    const start = window.performance.now();
    for (let i = 0; i < n; i++) {
      setTimeout(() => {
        const result = Math.floor((i / n) * 100);
        progress3.value = result;
        display_progress3.innerText = result;
      }, 0);
    }
    const end = window.performance.now();
    time3.innerText = `${end - start} ms`;
  }

 

결과

속도 측면에서는 setTimeout()을 이용하는 반복문이 가장 빨랐다.

그 이유는 setTimeout()를 사용한 onClickStart3()함수는 for문이 모두 종료된 후 setTimeout()이 실행됐기 때문이다.

 for문이 진행되는 동안 렌더링을 하지 않고 있다가  for문을 끝난 후에 밀려있던 모든 setTimeout()이 한 번에 처리되었다.

나머지 두 함수는 실제 진행속도에 맞게 progress가 같이 올라갔다.

예상했던대로 간단한 연산에서 requestAnimationFrame()는 브라우저 렌더링 주기마다 호출되기 때문에 매우 느리다.

 

if문을 이용하여 setTimeout()문을 감싸면 원하는 조건일 때만 변경할 수 있을 것 같다.

하지만 

속도 비교: setTimeout >>>> setInterval > requestAnimationFrame