JavaScript 비동기 핵심 Event Loop 정리

Jeongkuk Seo
sjk5766
Published in
9 min readMay 13, 2020

자바스크립트는 단일 스레드 기반으로 비동기로 동작하나요?

  • 네. 자바스크립트는 단일 스레드 기반의 언어로 한 순간 하나의 작업만을 처리할 수 있습니다.
  • 네. 자바스크립트는 비 동기로 동작하기 때문에 단일 스레드에도 불구하고 동시에 많은 작업을 수행합니다.

헌데.. 자바스크립트 언어 자체가 비동기 동작을 지원하는 것은 아닙니다.

비 동기로 동작하는 핵심요소는 자바스크립트 언어가 아니라 브라우저가 가지고 있습니다. (Node에서는 libuv 라이브러리 등)

브라우저는 Web APIs, Event Table, Callback Queue, Event Loop 등으로 구성되며 자바스크립트 코드가 실행될 때 브라우저와의 동작은 아래 그림으로 표현할 수 있습니다.

구성요소

  • Heap: 메모리 할당이 발생하는 곳
  • Call Stack : 실행된 코드의 환경을 저장하는 자료구조, 함수 호출 시 Call Stack에 push 됩니다. (Call Stack에 대한 자세한 설명은 여기)
  • Web APIs: DOM, AJAX, setTimeout 등 브라우저가 제공하는 API
  • Callback Queue, Event Loop, Event Table(그림엔 없음) 은 아래에서 설명하겠습니다.
setTimeout(function exec() {
console.log('second')
}, 1000);

위 코드가 실행될 때 각 구성요소 들이 어떤 역할을 하는지 보겠습니다.

  • Web APIs: setTimeout이 Call Stack에 들어와 실행되면 Browser API인 timer를 호출합니다.
  • Event Table: 특정 event(timeout, click, mouse move 등등)가 발생했을 때 어떤 callback 함수가 호출되야 하는지를 알고 있는 자료구조입니다. 위 코드에서 호출된 timer가 종료되면 event가 발생하게 되는데 이때 exec callback 함수가 실행되어야 한다는 것Event Table이 알고 있습니다.
  • Callback Queue: 이벤트 발생 시 실행해야 할 callback 함수가 Callback Queue에 추가됩니다.
  • Event Loop: Event Loop의 역할은 간단합니다.
    1. Call StackCallback Queue를 감시합니다.
    2. Call Stack이 비어있을 경우, Callback queue에서 함수를 꺼내 Call Stack에 추가 합니다.

예제를 보면서 확인하도록 하겠습니다.

위 코드가 실행될 때 각 구성요소들이 어떻게 동작하는지 순서대로 보겠습니다.

1.console.log(‘first’)가 Call Stack에 추가(push) 됩니다.

2. console.log(‘first’)가 실행되어 화면에 출력한 뒤, Call Stack에서 제거(pop) 됩니다.

3.setTimeout(function cb() {..}) 이 Call Stack에 추가됩니다.

4. setTimeout 함수가 실행되면서 Browser가 제공하는 timer Web API 를 호출합니다. 그 후 Call Stack에서 제거됩니다.

5. console.log(‘third’)가 Call Stack에 추가됩니다.

6. console.log(‘third’)가 실행되어 화면에 출력되고 Call Stack에서 제거됩니다.

7. setTimeout 함수에 전달한 0ms 시간이 지난뒤 Callback으로 전달한 cb 함수가 Callback Queue에 추가됩니다. (이 부분은 다른예시와 함께 뒤에서 설명하겠습니다.)

8. Event LoopCall Stack비어있는 것을 확인하고 Callback Queue를 살펴봅니다. cb를 발견한 Event Loop Call Stack에 cb를 추가합니다.

9. cb 함수가 실행 되고 내부의 console.log(‘second’)가 Call Stack에 추가됩니다.

10. console.log(‘second’)가 화면에 출력되고 Call Stack에서 제거됩니다.

11. cb가 Call Stack에서 제거됩니다.

위 일련의 흐름을 통해 JavaScript와 Browser의 구성요소들이 어떻게 동작하는지 이해하리라 생각합니다.

Event Loop를 설명 할 때

Call Stack이 비어있을 경우, Callback queue에서 함수를 꺼내 Call Stack에 추가 합니다

Call Stack이 비어있을 경우. 이와 관련된 예제를 보겠습니다.

위 코드의 핵심은 setTimeout과 7번 line의 wait3Seconds 함수입니다. line 2에서 setTimeout 함수에 delay 0을 주고 line 7에서 3초를 기다립니다. console.log 결과는 어떻게 될까요?

wait3Seconds 함수에 의해 3초 동안 console.log(‘third’)는 실행되지 않습니다. 따라서 0ms 후에 실행된 console.log(‘second’)가 먼저 출력되지 않을까요?

결과는 아래와 같습니다.

first
third
second

wait3Seconds 함수가 실행된 후 3초 동안 Call Stack 및 Event Loop 상태입니다.

setTimeout이 호출된 후 0ms 뒤에 callback으로 전달 된 cb는 이미 Callback Queue 안에 있었습니다. 그럼에도 불구하고 console.log(‘second’)가 third보다 뒤에 출력되는 이유는 Call Stack에 wait3Seconds함수가 있기 때문입니다.

Event LoopCall Stack이 비어있지 않기 때문에(wait3Seconds)
Callback Queue를 체크하지 않습니다. wait3Seconds 함수 종료 후엔 console.log(‘third’)Call Stack에 추가되어 먼저 출력됩니다.

위 예제를 통해 한 가지 확인할 수 있는 것은 setTimeout의 delay인자가 delay ms 후에 실행 되는 것을 보장하지 않는다는 겁니다. 정확히는 delay ms 후에 Callback Queue에 들어가는 것을 보장합니다.

Event Loop를 포함해 Browser의 구성요소 역할을 이해했다면, 자바스크립트 언어 자체비동기 특성을 제공하는게 아니라 Browser의 구성 요소들이 제공하는 것을 이해할 수 있어야 합니다.

ES6 Job Queue (…microtask queue..??)

ES6/ES2015 에서 소개된 Job QueueCallback Queue와 다른 Queue이며 Promise를 사용할 경우 Job Queue를 사용하게 됩니다. promise를 사용할 때
callback 함수 역할을 하는 .then 을 사용하게 되며, 이런 thenable한 함수들은 Job Queue에 추가됩니다.

두 Queue는 우선순위가 다른데 아래 코드 결과를 같이 볼까요.

코드의 결과입니다.

fisrt
five
promise - third
promise - four
setTimeout - second

결과를 통해 아시겠지만 Job Queue의 우선순위가 Callback Queue보다 높습니다. 따라서 Event LoopCall Stack이 비어있을 경우, Job Queue에서 기다리는 모든 작업을 처리하고 Callback Queue로 이동하게 됩니다.

Note - HTML Spec에서 microtask queue라는 개념이 있으며 Promise의 thenable 메소드가 microtask queue에 들어간다고 설명하는 글 들도 많습니다. (대표적으로 유명한 이글, Tasks, microtasks, queues and schedules)

HTML Spec의 microtask queue와 ES Spec의 Job Queue의 연관성이 모호하여 이 파트부분 소개를 microtask queue로 할지 Job Queue 할지 조금 고민했습니다.

글을 쓰는 시점 기준으로 ES 최신 2019 Spec에도 여전히 Job Queue 용어로 표현되므로 Job Queue로 소개한 것을 참고 부탁드립니다.

https://www.ecma-international.org/ecma-262/10.0/index.html 발췌

참고 자료

https://medium.com/front-end-weekly/javascript-event-loop-explained-4cd26af121d4

https://blog.sessionstack.com/how-javascript-works-event-loop-and-the-rise-of-async-programming-5-ways-to-better-coding-with-2f077c4438b5

https://hackernoon.com/understanding-js-the-event-loop-959beae3ac40

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

--

--