2020. 10. 6. 22:24ㆍFrontend
Interview Section은 FrontEnd Interview Question에 대한 QnA를 다룹니다.
Q. What is Event Loop? (+ What is the difference between call stack and task queue?)
A.
이벤트 루프(Event Loop)는 자바스크립트가 비동기 액션들(async, await, setTimeout, mouseClick)을 처리하는 방법을 제공합니다. 자바스크립트는 콜 스택이 하나입니다. 즉, 한번에 하나의 코드만 실행할 수 있다는 의미입니다. 이 때문에 "자바스크립트는 싱글 스레드 언어이다" 라고 이야기하기도 하는데, 엄밀히 말하면 이는 코드를 실행하는 콜 스택이 하나라는 의미입니다. (실제로 비동기 액션들을 처리하는 자바스크립트 런타임은 멀티스레드를 사용하는 경우가 대부분입니다.)
자바스크립트가 한번에 하나의 함수만 실행할 수 있기 때문에, 언제 응답이 올지 모르는, 그리고 대개 동시성 코드에 비해 훨씬 많은 시간을 소모하는 비동기 코드를 콜 스택에서 실행하는 것은 프로그램을 매우 느리게 만듭니다. (서비스 품질을 떨어뜨립니다.) 따라서 자바스크립트는 비동기 코드를 이벤트 루프를 통해 실행합니다.
이벤트 루프는 비동기 코드(함수)를 받아서 태스크 큐(Task Queue)에 집어넣고, 콜 스택에 쌓인 함수가 모두 호출된 후 콜 스택이 빈 상태가 되면, 이 태스크 큐에서 들어온 순서대로 비동기 함수를 실행합니다. 자바스크립트 런타임은 동시성 코드를 한줄한줄 실행하다가 비동기 코드를 만나게 되면, 이를 콜 스택에 올리는 것이 아니라, 이벤트 루프에 던집니다. 이벤트 루프는 이 비동기 코드들(함수들)을 태스크 큐에 던지고 콜 스택에 쌓여있는 함수들이 모두 실행되고 나서 콜 스택이 비게 되면, 태스크 큐에 쌓인 순서대로 작업을 수행하는 것입니다.
이벤트 루프를 이용하기 때문에 자바스크립트는 Non-Blocking 한 언어라고도 불리게 됩니다. Non-Blocking하다는 것은 비동기 코드를 실행하기 위해 사용자 프로세스를 멈추지 않아도 된다는 의미인데, 자바스크립트는 비동기 코드를 실행하기 위해서 기존 콜 스택에서 실행되던 사용자 프로세스를 멈추는 것이 아니라, 우선 이벤트 루프에 던져놓고, 콜 스택이 비면 이를 꺼내서 호출하는 구조이므로 사용자 프로세스를 멈추지 않습니다.
다음의 예시는 자바스크립트가 이벤트 루프를 이용하여 어떤 순서대로 작업을 진행하는지를 보여줍니다.
setTimeout(() => console.log("Event Loop, Macro Task"));
Promise.resolve().then(() => console.log("Event Loop, Micro Task"));
console.log("Call Stack");
- setTimeout은 비동기 코드이기 때문에 콜백함수는 이벤트 루프에 던져져 태스크 큐에 들어가게 됩니다.
- Promise 도 비동기를 위한 코드이므로 then 안에 들어가는 콜백함수도 이벤트 루프에 던져져 태스크 큐에 들어가게 됩니다.
- console.log는 동시성 코드이기 때문에 이벤트 루프에 들어가지 않고 콜 스택에서 호출됩니다.
따라서 콜스택에서 실행되는 console.log가 먼저 실행된 후, 태스크 큐 안에 있는 콜백 함수들이 실행되는 것입니다.
Macro Task, Micro Task
하지만 위의 예시를 자세히 보면 setTimeout의 콜백보다 Promise Resolve에 정의된 콜백이 먼저 실행된 것을 알 수 있습니다. 큐는 FIFO(First in First Out)의 특성을 가진 자료구조이기 때문에 먼저 들어간 setTimeout의 콜백이 먼저 실행되어야 하지만, 실제로는 그렇게 동작하지 않는 것처럼 보이는데, 이유는 태스크에도 우선순위가 있기 때문입니다.
이벤트 루프는 콜스택이 빈 후 태스크 큐에 있는 코드를 실행할 때, 이를 Macro Task와 Micro Task로 구분합니다. 자바스크립트는 스펙상, 하나의 매크로 태스크를 수행하기 전에 쌓여있는 모든 마이크로 태스크를 실행합니다. Promise.resolve의 콜백은 마이크로 태스크에 속하고, setTimeout은 매크로 태스크에 속하기 때문에 Promise의 콜백 함수가 먼저 실행된 것입니다.
자바스크립트에서는 자체 메서드를 통해 특정 비동기 함수를 마이크로 태스크로 지정하여 우선순위를 부여하는 방법을 제공합니다.
queueMicrotask를 이용하면 우선순위를 지정해서 함수를 실행할 수 있습니다.
자바스크립트에서 이벤트 루프를 사용한 비동기 처리를 이해하는 것은 굉장히 중요합니다. 간단한 타이머를 만들때 조차 setTimeout, setInterval등의 함수가 실제 시간과는 다르게 동작할 때가 있는데, 이는 대부분의 경우 이벤트 루프에 의해 실행 시점이 미뤄지기 때문입니다.
Reference
'Frontend' 카테고리의 다른 글
[Browser] 웹 페이지 로드 과정 (0) | 2020.10.27 |
---|---|
[CSS] CSS Sprite란? (0) | 2020.10.07 |
[Javascript] let, var, const (0) | 2020.10.04 |
[Javascript] 함수 호이스팅 (0) | 2020.10.03 |
[Javascript] null vs undefined (0) | 2020.10.03 |