자바스크립트는 기본적으로 단일쓰레드에서 동작한다.

하지만 주로 웹에 쓰이는 언어다보니, 단일쓰레드에서 동기적으로만 동작한다면 이런저런 문제가 발생할 수 있다.

외부에서 용량이 큰 데이터를 받아서 띄워주는 경우를 예로 들어보자.

만약 데이터를 받는 시간이 밀리세컨드 단위라면 동기적으로 실행되어도 큰 문제는 없을 것이다.

하지만 받는 시간이, 1, 2초나 그 이상의 시간이 걸리는 경우, 최악의 경우엔 데이터 파일 하나 때문에 웹페이지 전체를 띄우지 못하는 경우가 생길 수 있다.

이러한 문제상황에 대비해서, 자바스크립트에서는 단일쓰레드에서도 비동기적인 프로그래밍이 가능하도록 몇가지 장치를 해두었는데, 그것이 Event QueueEvent Loop이다.


Call Stack

사실 Call Stack은 비단 자바스크립트가 아니더라도 대부분의 언어에 존재하는 개념이다.

함수가 시작되면 CallStack에 해당 함수가 쌓인다. 이 때 쌓이는 구조는, 이름답게 자료구조의 스택과 동일하다.

만약 함수가 종료되면 해당 함수는 CallStack에서 제거된다.

함수가 종료되기 전에 또다른 함수가 실행되면, 해당함수는 종료되지않은 기존 함수스택 위에 새로운 스택으로서 쌓이게 된다.

사실 동기적인 프로그램만 구현한다고 가정한다면, 아래 요소들 없이 CallStack만으로도 충분히 원하는 프로그램을 작성할 수 있다.

하지만 비동기적인 프로그램을 구현한다고 한다면, 콜 스택과는 다른 공간에서 동작하는 무언가가 필요할 것이다.

Event Queue & Event Loop

자바스크립트의 Event QueueEvent Loop는 이러한 상황을 해결할 수 있게 해준다.

일반적으로 자바스크립트에서 "콜백 함수"로 실행된 함수들은 실행과 함께 별도의 WebApi로 보내져서 동작한다.

그리고 동작이 끝나면 Event LoopEventQueue에 에 있는 함수들을 차례로 실행시킨다.

Mdn에서는 Event Queue에 대한 동작을 위와 같이 정의하고 있다.

while(queue.waitForMessage()){
  queue.processNextMessage();
}

이 때, queue.waitForMessage() 함수는 현재 아무 메시지도 없다면 새로운 메시지 도착을 동기적으로 기다린다.

만약, 대기열에 메세지가 없는데 새로운 메세지가 들어왔다면, 곧바로 해당 메세지가 실행된다.
하지만 기존에 다른 메세지가 기존에 존재한다면, 먼저 있던 메세지들의 동작이 다 끝나야 다음 메세지가 동작할 수 있다.

이러한 동작은 다음과 같은 결과에서 다시 확인할 수 있다.

Zero Delay

  setTimeout(funcA, 0);

위 setTimeout 함수는 특이하게 대기시간이 0이다.

이것만 보면 마치 0초뒤에 바로 함수가 실행되어야 할것 같지만 그렇지않다.
다음 두가지 이유 때문이다.

이것만 보면 마치 0초뒤에 바로 함수가 실행되어야 할것 같지만 그렇지않다. 다음 두가지 이유 때문이다.

  1. 콜스택에 이미 다른 함수가 쌓여있다.
  2. Event Queue에 먼저 온 메세지가 쌓여있다.

다음 두가지 경우에 자바스크립트는 쌓여있는 콜스택을 최우선으로 처리하고, 이후 먼저온 메세지를 처리한다.

정리하자면 setTimeout에서 정의한 시간은 최소지연시간을 나타낼 뿐, 정확한 동작시간을 보장하지는 않는다는 것이다.

결론

Event LoopEvent Queue를 통한 비동기적인 동작은 제대로 사용했을 경우 이점이 굉장히 많다.

하지만 단점 역시 명확하다.
위의 경우처럼 스택이나 메세지에 지연시간이 긴 함수가 동작한다면 원하는 시간에 원하는 Event가 동작하지 않을 수 있다.

특히 만약 그 동작이 화면 렌더링이나, 스크롤에 관련된 동작이라면 더욱 치명적일 것이다.

Mdn에서 추천되는 해결방안은, 메세지 처리를 짧게 만들거나, 메세지를 여러개로 분할하는 것이다.

이렇게 하면 미미한 오버헤드가 생길지언정, 특정 긴 이벤트 때문에, 중요한 이벤트가 지연되는 최악의 상황은 방지될 수 있을것이라 생각한다.

참조

https://developer.mozilla.org/ko/docs/Web/JavaScript/EventLoop