Event Loop

자바스크립트는 단일 스레드 프로그래밍 언어이며, 단일 스레드 런타임이기도 하고, 단일 호출 스택이 있다. 그래서 한 번에 한 가지 일만 할 수 있다. 여러 일을 한번에 할 수 없기 때문에 모든 것을 동기적으로 처리하는데, 여기에서 어떻게 벗어날 수 있을까?

동기적 작업의 문제점

간단한 예제만 만들어서 테스트 하여도 문제는 분명하다. 브라우저에서 동기적 작업이 실행되었을 때, 선행 작업의 시간이 오래 걸린다면 그 사이에 사용자는 아무것도 할 수가 없다.

루프 버튼을 누르고 내부에서 while 루프가 실행되는 순간, 그것이 끝나기 전까지 브라우저가 차단되고 그 요청이 완료될 때까지 아무것도 할 수가 없다. 이런 현상은 곧바로 최악의 UX를 가져다 줄 것이다.

자바스크립트를 사용하지만, 비동기 작업이 필요하다!

위의 예처럼 모든 코드가 동기적으로 작동된다면, 시간이 걸리는 일을 처리한다면 블로킹(blocking)이 발생한다. 그래서 아래와 같이 setTimeout이라는 브라우저(혹은 node.js)에서 제공하는 Web API(혹은 libuv)를 이용하여 싱글 쓰레드 언어인 자바스크립트에서 동시성(Concurrency)이 지원되게 만들어 준다.

console.log("jacob");

setTimeout(function() {
  console.log("world!");
}, 1000);

console.log("Hello");

그리고 단일 호출 스택을 사용하는 자바스크립트 엔진과 멀티 스레드를 사용하는 자바스크립트가 구동되는 환경(브라우저, node)에서 상호 연동하기 위해 사용하는 장치가 Event Loop인 것이다.

이벤트 루프(Event Loop)

이벤트 루프(Event Loop)는 콜백 큐와 호출 스택을 모두 모니터링하고 지속적으로 실행되는 프로세스이다. 호출 스택이 비어 있지 않으면, 이벤트 루프는 비어 있을 때까지 기다렸다가 콜백 대기열에서 호출 스택으로 다음 함수를 배치하게 된다.

프로세스(Process) vs 스레드(Thread)

  • 프로세스란 컴퓨터에서 연속적으로 실행되고 있는 프로그램을 말한다. 메모리에 올라와 실행되고 있는 프로그램 인스턴스라고 보면 된다. 운영체제로부터 시스템 자원을 할당받는 작업의 단위이다.

  • 스레드란 프로세스 내에서 실행되는 여러 흐름의 단위라고 할 수 있다. 프로세스가 할당받은 자원을 이용하는 실행의 단위라고 보면 된다.

  • 항상 호출 스택(LIFO, 후입선출)과 콜백 큐을 관찰하고 있다.

// mdn EventLoop 설명(가상 코드)
while(queue.waitForMessage()){
  queue.processNextMessage();
}
  • 대기열에서 가장 오래된 메시지부터 순차적(FIFO) 처리하기 시작한다.

  • 호출스택이 비어있을때, 먼저 들어온 순서대로 콜백큐에 있는 콜백함수들을 호출 스택으로 집어 넣는다.

  • 크롬 기준으로 봤을때, 이벤트 루프는 마이크로 태스크 큐 -> 애니메이션 프레임 -> 콜백큐 순서로 큐 안에 있는 task를 확인하고 태스크가 있다면 빼서 호출 스택에 추가한다.

Call Stack(호출 스택)

작업이 요청되면(함수가 호출되면) 요청된 작업은 순차적으로 호출 스택에 쌓이게 되고 순차적으로 실행된다. 자바스크립트는 단 하나의 호출 스을 사용하기 때문에 해당 작업 종료하기 전까지는 다른 어떤 작업도 수행될 수 없다.(Run-to-completion)

Tick

호출 스택이 비어있는 경우, 이벤트 루프는 대기열에서 첫번째 이벤트를 가져와 호출 스택으로 푸쉬(push)한다. 이러한 반복을 이벤트 루프에서 틱(tick)이라고 한다.

Callback Queue

콜백큐(callback queue)는 태스크큐(task queue), 이벤트큐(event queue) 등으로 불린다. setTimeout이나 setInterval과 같은 비동기 함수의 콜백 함수 또는 이벤트 핸들러가 일시적으로 보관되는 영역이다.

Microtask queue

위 그림에는 없지만 ES6에서 새롭게 도입된 개념인 마이크로 태스크 큐(microtask queue)도 존재한다. 잡큐(job queue)라고도 불린다.

마이크로 태스크큐는 이벤트 루프 대기열의 맨 위에 있는 레이어이다. 이 큐에는 프로미스의 후속 처리 메서드의 콜백 함수가 일시 저장된다. 프라미스 객체의 .then, .catch, .finally 콜백 함수가 보관되는 영역이라고 보면 된다.

콜백 함수나 이벤트 핸들러를 일시 저장한다는 점에서 태스크 큐(콜백 큐)와 동일하지만, 마이크로 태스크 큐는 태스크 큐보다 우선순위가 높다. 즉 이벤트 루프는 호출 스택이 비면, 먼저 마이크로 태스크큐에서 대기하고 있는 함수를 가져와 실행하고, 이후 마이크로 태스크 큐가 비면 태스크 큐에서 대기하고 있는 함수를 가져와 실행한다.

Animation Frames

위 그림에는 없지만, requestAnimationFrame 의 콜백 함수가 보관되는 영역이다.

Last updated