이벤트 루프
- Run-to-Completion : 자바스크립트의 함수가 실행되는 방식. 하나의 함수가 실행되면 이 함수의 실행이 끝날 때까지는 다른 어떤 작업도 중간에 끼어들지 못함
- 자바스크립트는 ‘단일 스레드’ 기반의 언어인데 웹 브라우저가 애니메이션 효과를 보여주면서 사용자 이벤트를 받을 수 있는 이유 ? 이벤트 루프를 이용해 비동기 방식으로 자바스크립트의 동시성(Concurrency) 을 지원하기 때문
- 동시성에 대한 처리 : 자바스크립트 엔진을 구동하는 환경 (브라우저, Node.js)
브라우저 환경
Call stack
소스코드(전역 코드나 함수 코드) 평가 과정에서 생성된 실행 컨텍스트가 추가되고 제거되는 스택 자료구조
-
함수의 실행 순서는 콜 스택으로 관리된다
- 함수 호출 → 함수 코드 평가 → 함수 실행 컨텍스트 생성 → 실행 컨텍스트 스택 (=콜스택) 에 Push 됨 (함수 실행의 시작) → 함수 코드 실행 → 함수 코드 실행 종료 시 콜스택에서 pop 되어 제거됨
-
자바스크립트 엔진은 단 하나의 실행 컨텍스트 스택을 가짐 ⇒ 싱글 스레드 방식의 동작
- 동시에 2개 이상의 함수를 실행할 수 없다
- 처리에 시간이 걸리는 태스크를 실행하는 경우 blocking 발생
- 최상위 실행 컨텍스트가 종료되어 콜스택에서 제거되기 전까지는 다른 태스크가 실행되지 않음
Heap
- 콜 스택의 요소인 실행 컨텍스트가 참조하는 객체가 저장되는 메모리 공간
- 객체는 원시값과 달리 할당해야할 메모리 공간의 크기가 런타임에 결정(동적 할당)됨 ⇒ 힙은 구조화 되어있지 않음!
Web API
Task Queue
- setTimeout , setInterval 과 같은 비동기 함수의 콜백 함수 또는 이벤트 핸들러가 일시적으로 보관되는 영역 cf) 프로미스의 후속 처리 메서드가 일시적으로 보관되는 영역은 마이크로태스크 큐
Event Loop
- 콜 스택에 현재 실행중인 실행 컨텍스트가 있는지 , 태스크 큐에 대기중인 함수가 있는지 반복해서 확인한다.
- 콜스택이 비어있고 태스크 큐에 대기중인 함수가 있다면 이벤트 루프는 순차적으로 태스크 큐에 대기중인 함수를 콜스택으로 이동시킨다.
- “자바스크립트는 싱글 스레드로 동작한다” 라는 말의 찐 의미 : 브라우저에 내장된 자바스크립트 엔진은 싱글 스레드, 브라우저는 멀티 스레드
Node.js 환경
- Node.js는 비동기 IO를 지원하기 위해 libuv 라이브러리를 사용하며, 이 libuv가 이벤트 루프를 제공
- 자바스크립트 엔진은 비동기 작업을 위해 Node.js의 API를 호출하며, 이때 넘겨진 콜백은 libuv의 이벤트 루프를 통해 스케쥴되고 실행됨
setTimeout(fn,0)
- setTimeout 함수는 콜백 함수를 바로 실행하지 않고 태스크 큐에 추가한다.
setTimeout(function() {
console.log('A');
}, 0);
console.log('B');
Promise 와 이벤트 루프
Micro Task
- 현재 실행되고 있는 작업 바로 다음으로 실행되어야 할 비동기 작업. 일반 태스크보다 더 높은 우선순위를 갖는 태스크
- 태스크 큐에 대기중인 태스크가 있더라도 마이크로 태스크가 먼저 실행됨
- 태스크를 기다리기 전에 마이크로 태스크가 있는지를 먼저 확인하고, 마이크로 태스크가 있다면 먼저 모두 수행하고 나서 태스크를 수행함
ex)
setTimeout(function() {
console.log('A');
}, 0);
Promise.resolve().then(function() {
console.log('B');
}).then(function() {
console.log('C');
});
setTimeout()
: 콜백 A를 태스크 큐에 추가
- Promise의
then()
메소드 : 는 콜백 B를 태스크 큐가 아닌 별도의 마이크로 태스크 큐에 추가
- 위의 코드의 실행이 끝나면 태스크 이벤트 루프는 태스크 큐 대신 마이크로 태스크 큐가 비었는지 먼저 확인하고, 큐에 있는 콜백 B를 실행
- 콜백 B가 실행되고 나면 두번째 then() 메소드가 콜백 C를 마이크로 태스크 큐에 추가
- 이벤트 루프는 다시 마이크로 태스크를 확인하고, 큐에 있는 콜백 C를 실행한다.
- 마이크로 태스크 큐가 비었음을 확인한 다음 태스크 큐에서 콜백 A를 꺼내와 실행
위 상황처럼 마이크로 태스크이냐 일반 태스크이냐에 따라 실행되는 타이밍이 달라지기 때문에 마이크로 태스크가 계속돼서 실행될 경우 일반 태스크인 UI 렌더링이 지연되는 현상이 발생할 수 있다.
References
https://meetup.toast.com/posts/89
https://dev.to/lydiahallie/javascript-visualized-event-loop-3dif
모던 자바스크립트 Deep Dive