JavaScript Runtime - Event Loop, Callback Queue
앞선 JavaScript Runtime 포스팅에서는 JavaScript 런타임의 구조를 다음과 같이 이해했다(Figure1).
이번 포스트에서는 event loop와 callback queue를 중심으로 보다 구조를 세분화해서 살펴보도록하자. 이벤트 루프와 의 세부적인 구조를 가장 잘 시각화한 자료가 있어 소개한다(Figure2).
각각의 약어들이 의미하는 바는 다음과 같다:
- T (Task)
- rAF(`requestAnimationFrame`)
- S (Style Computation)
- L (Layout)
- P (Painting)
대기중인 작업이 없을 때 이벤트 루프는 가운데 원을 공회전한다. 그러다가 태스크가 쌓이면 좌측 바깥쪽 원을, 렌더링이 필요한 경우에는 우측 바깥쪽 원 둘레를 돌면서 대기중인 작업들을 처리할 수 있도록 한다. 위 애니메이션에서 흰색 네모 박스와 닿는 태스크들은 Call Satck에 넘겨져 처리된다고 이해해볼 수 있다.
Event Loop
이벤트 루프의 역할은 callback queue에 쌓인 태스크들을 계속해서 자바스크립트 엔진에 공급하는 것이다. 이 때 태스크들의 성격에 따라 자바스크립트 엔진에 공급되는 방식과 순서가 달라진다.
Callback Queue
Callback Queue는 태스크의 종류에 따라 다시 (macro)task queue와 microtask queue로 나뉜다.
(Macro)Task Queue
- (macro) task에 해당하는 작업들은 다음과 같다:
-<script src="...">
등을 통해 외부에서 로드하는 스크립트
-mousemove
이벤트를 dispatch하는 유저 인터랙션
-setTimeout()
,setInterval()
과 같이 타이머를 세팅하는 태스크 - 기존에 큐에 쌓여 있던 태스크들 가운데 가장 먼저 쌓인 것 하나만 처리한다. 그 다음 태스크를 처리하려면 다음 주기까지 기다려야한다.
- 새로운 태스크가 추가되면 큐의 맨 뒤에 줄세운다.
Microtask Queue
- 기존에 큐에 쌓여있던 모든 태스크를 처리한다.
- 기존 태스크를 처리하는 동안 새로운 태스크가 추가되면 큐의 맨 뒤에 줄 세우고 다음 주기에 처리한다.
Rendering
화면을 새로 그리는 렌더링 작업 역시 큐에 줄서있는 콜백들과 함께 event loop가 자바스크립트 엔진에게 태스크를 전달하는 주기에 따라 이루어진다. 일반적으로 모니터와 노트북과 같은 디스플레이는 1초인 1000ms동안 60번 렌더링을 하므로 렌더링 함수는 1000을 60으로 나눈 약 16.6ms마다 한번씩 호출된다. 그런데 렌더링 역시 앞서 말한 태스크들과 마찬가지로 call stack이 비어있는 경우에만 처리될 수 있다. 따라서 정확히 말하면 16ms마다 실제 렌더링이 이루어지는 것이 아니라 최소 16ms마다 렌더링이 일어날 수 있도록 일종의 대기열에 줄을 세우는 것이다. 실제로 렌더링은 여러 요소들에 영향을 받기때문에 16ms 내에 여러차례 일어나거나 16ms 내에 한번도 이루어지지 않아서 버벅거리는 느낌을 주기도 한다.
References
Archibald, Jake. (2018, January 26). "In The Loop" [video file]. JS Conf. Asia, Singapore. Retrieved from https://youtu.be/cCOL7MC4Pl0
Modern JavaScript Tutorial - Event loop: microtasks and macrotasks