프론트엔드 환경의 자바스크립트 코드를 보다 보면 setTimeout(fn, 0) 와 같은 코드를 종종 보게 된다. 관용적으로 쓰이는 코드이지만, 사실 처음 보는 사람에게는 직관적으로 이해하기 힘든 코드일 것입니다.

0초 이후에 실행을 한다는 건 실제로 그냥 실행하는 것과 다를 게 없으니 말이다. 하지만 실제로 이 코드는 그냥 fn을 실행하는 것과는 상당히 다른 결과를 가져오게 됩니다. 이전 예제 에서 보았겠지만 setTimeout 함수는 콜백 함수를 바로 실행하지 않고 (호출 스택이 아닌)Task Queue에 추가하게 됩니다. 그렇기 때문에 아래의 코드는 콘솔에 B → A 순서로 출력하게 됩니다.

setTimeout(function() => {
	console.log('A');
}, 0);
console.log('B');

프론트엔드 환경에서는 렌더링 엔진과 관련해서 이런 코드가 특히 요긴하게 쓰일 때가 있습니다. 브라우저 환경에서는 자바스크립트 엔진뿐만 아니라 다른 여러 프로세스가 함께 구동되고 있습니다. 렌더링 엔진도 그 중 일부이며, 이 렌더링 엔진의 태스크는 대부분의 브라우저에서 자바스크립트 엔진과 동일한 단일 Task Queue를 통해 관리됩니다.

이로 인해 가끔 예상치 못한 문제가 생길 결우가 있는데, 다음의 코드를 살펴보겠습니다.

$('.btn').click(function() {
    showWaitingMessage();
    longTakingProcess();
    hideWaitingMessage();
    showResult();
});

*longTakingProcess가 너무 오래 걸리는 작업이기 때문에 그 전에 showWaitingMessage 를 호출해서 로딩 메시지(’로딩 중…’과 같은)를 보여주려고 합니다. 하지만 실제로 이 코드를 실행해 보면 화면에 로딩 메시지가 표시되는 일은 없을 것입니다.*

이유는 showWaitingMessage 함수의 실행이 끝나고 렌더링 엔진이 렌더링 요청을 보내도 렌더링 요청은 Task Queue에서 이미 실행중인 태스크가 끝나기를 기다리고 있기 때문입니다.

실행중인 태스크가 끝나는 시점은 호출 스택(Call Stack)이 비워지는 시점인데, 그 때는 이미 showResult 까지 실행이 끝나 있을 것이고, 결국 렌더링이 진행되는 시점에는 hideWaitingMessage 로 인해 로딩 메시지가 숨겨진 상태일 것입니다.

이를 위해서 다음처럼 setTimeout을 사용할 수 있습니다.

$('.btn').click(function() {
    showWaitingMessage();
    setTimeout(function() {
        longTakingProcess();
        hideWaitingMessage();
        showResult();
    }, 0);
});

이 경우에는 longTakingProcess 가 바로 실행되지 않고 Task Queue에 추가될 것입니다. 하지만 showWaitingMessage로 인해 Task Queue에는 렌더링 요청이 먼저 추가되기 때문에 longTakingProcess는 그 다음 순서로 Task Queue 에 추가될 것입니다.

이제 Event Loop는 Task Queue에 있는 렌더링 요청을 먼저 처리하게 되고 로딩 메시지가 먼저 화면에 보여지게 됩니다.

꼭 렌더링 관련이 아니라도, 실행이 너무 오래 걸리는 코드를 setTimeout을 사용하여 적절하게 다른 태스크로 나누어 주면 전체 어플리케이션이 멈추거나 스크립트가 너무 느리다며 경고창이 뜨는 상황을 방지할 수도 있을 것입니다.