JavaScript 이벤트 루프


자바스크립트의 여러가지 특징 중 하나는 싱글 스레드 언어라는 점입니다. 즉 자바스크립트는 하나의 호출 스택을 가지고 한 번에 한 가지 일을 처리할 수 있습니다. 하지만 실제로 자바스크립트를 사용할 때는 동시에 여러가지 작업을 처리하는 것처럼 보입니다. 이것을 가능하게 해주는 것이 이벤트 루프입니다.

이벤트 루프란?

이벤트 루프는 정확히는 자바스크립트에 포함된 기능이 아니라 자바스크립트의 실행 환경, 브라우저나 Node.js에서 제공하는 기능입니다. 자바스크립트에서 비동기 API를 호출하면 비동기 작업이 완료된 후 태스크 큐(Task Queue)에 콜백 함수를 enqueue합니다. 그리고 이벤트 루프는 현재 호출 스택이 비어있는지를 확인하고(=현재 실행중인 코드가 없는 것을 확인하고) 태스크 큐에서 콜백 함수를 순서대로 호출합니다. 이 방식으로 자바스크립트는 싱글 스레드로 동시성(Concurrency)를 제공합니다.

이 중에서도 특히 호출 스택이 빈 이후에 태스크 큐에서 함수를 호출한다는 특징 때문에 아래와 같이 setTimeout을 실행했을 때 비직관적인 결과가 나오기도 합니다.

console.log('A');

setTimeout(() => console.log('B'), 0);

console.log('C');

// A
// C
// B

위 코드에서 setTimeout함수는 그냥 봤을 때는 0ms 이후에 함수를 실행하는 이상한 코드처럼 보입니다. 그리고 결과는 순서대로 A, B, C가 출력될 것처럼 보이지만 실제로 출력되는 것은 A, C, B 순서입니다. 이는 위에서 설명한 것처럼 setTimeout이 실행되면 바로 태스크 큐에 console.log('B')가 추가되지만(주의: setTimeout이 5번 이상 누적되면 delay의 최솟값이 4ms로 고정됩니다.) 호출 스택이 아직 비지 않았기 때문에 마지막 C를 출력하고 작업이 종료된 후에 콜백 함수가 실행됩니다. 이를 통해 setTimeout(cb, 0)은 최소 0ms 후에 콜백 함수를 실행하지 정확히 0ms후에 함수를 실행해주지 않는다는 것을 알 수 있습니다.

태스크와 마이크로태스크

위에서 이벤트 루프를 설명할 때 태스크 큐에 저장된 함수를 호출한다고 했는데 사실 태스크 큐 외에 마이크로태스크 큐(Microtask Queue)라는 것이 존재합니다. 그리고 이벤트루프는 마이크로태스크 큐의 마이크로태스크들을 우선적으로 호출합니다. 마이크로 태스크는 Promise에서 호출한 콜백 함수 또는 queueMicrotask()로 호출한 함수가 있습니다.

그럼 이제 아래 예제의 결과를 예상할 수 있을겁니다.

console.log('A');

setTimeout(() => console.log('B'), 0);

Promise.resolve().then(() => console.log('C'));

console.log('D');

// 콘솔: A D C B

위 코드의 실행순서는 다음과 같습니다.

  1. console.log('A');에서 콘솔에 A를 출력한다.
  2. setTimeout(() => console.log('B'), 0);에서 태스크 큐에 console.log('B')를 추가한다.
  3. Promise.resolve().then(() => console.log('C'));에섯 마이크로태스크 큐에 console.log('C')를 추가한다.
  4. console.log('D');에서 콘솔에 D를 출력한다. 스크립트 실행이 종료된다.
  5. 호출 스택이 비었으므로 우선 마이크로태스크 큐에 있는 함수를 호출한다 -> C를 출력한다.
  6. 마지막으로 태스크 큐에 있는 함수를 호출한다 -> B를 출력한다.

위 코드의 실행 순서를 이해했다면 이벤트 루프에 대해서 제대로 이해했다고 생각하시면 됩니다.

참고 자료

https://www.youtube.com/watch?v=8aGhZQkoFbQ

https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/

https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide/In_depth