이벤트 루프 정리
자바스크립트의 비동기 처리 방식
- javascript는 언어 단에서 비동기 처리를 지원하지 않는다.
- 하지만 네트워크 요청, 타이머 실행 등등 여러 작업들을 동기적으로 처리하기에는 어려움이 있다.
- 따라서 자바스크립트를 실행시켜주는 환경에서 비동기 처리를 지원해줘야 한다.
- 자바스크립트를 실행해주는 환경은 node.js, 브라우저 환경 등이 있는데 브라우저 환경에서는 비동기 처리를 지원하기 위해 이벤트 루프라는 방식을 활용해서 지원해준다.
자바스크립트 엔진의 메모리 구조
- 자바스크립트 엔진의 메모리 구조는 크게 힙과 콜 스택으로 이뤄진다.
- 힙은 객체가 저장되는 메모리 공간이다.
- 콜 스택은 소스코드 평가 과정에서 생성된 실행 컨텍스트가 추가되고, 제거되는 스택 자료구조이다.
- 함수를 호출하면 순차적으로 콜 스택에 푸시되어 실행된다.
- 자바스크립트 엔진에서는 단 하나의 콜 스택을 사용하기 때문에 최상위 실행 컨텍스트가 종료되어 콜 스택에서 제거되기 전까지는 다른 어떤 테스크도 실행되지 않는다.
Node.js, 브라우저 에서의 비동기 처리
- 앞서 말했듯, 자바스크립트 엔진에서는 콜 스택에 있는 작업들을 순차적으로 실행할 뿐이다.
- 비동기 처리에서 소스코드의 실행을 제외한 모든 처리는 Node.js나 브라우저가 담당한다.
- 이 때 활용되는 것이 테스크 큐와 이벤트 루프이다.
테스크 큐
- setTimeout이나, setInterval과 같은 비동기 함수의 콜백 함수, 또는 이벤트 핸들러가 일시적으로 보관되는 영역이다.
- 테스트 큐와는 별도로 프로미스의 후속 처리 메서드의 콜백 함수가 일시적으로 보관되는 마이크로테스크 큐도 존재한다.
이벤트 루프
- 이벤트 루프는 콜 스택에 현재 실행 중인 실행 컨텍스트가 있는지, 그리고 테스크 큐에 대기 중인 함수가 있는지 반복해서 확인한다. 만약 콜 스택이 비어있고 테스크 큐에 대기중인 함수가 있다면 이벤트 루프는 순차적으로 테스크 큐에 대기 중인 함수를 콜 스택으로 이동시킨다. 이때 콜 스택으로 이동되는 함수들이 실행된다.
비동기 처리 순서 예시
- 다음은 테스크 큐와 이벤트 루프를 활용해서 비동기가 처리되는 예시를 코드를 통해 알아볼 것이다.
console.log("1. [Sync] 시작");
// 1. Macrotask (태스크 큐)
setTimeout(() => {
console.log("2. [Macrotask] setTimeout (0ms)");
}, 0);
// 2. Microtask (마이크로태스크 큐)
Promise.resolve()
.then(() => {
console.log("3. [Microtask] Promise.then 1");
})
.then(() => {
console.log("4. [Microtask] Promise.then 2");
});
// 3. Animation Task (렌더링 파이프라인)
requestAnimationFrame(() => {
console.log("5. [Render] requestAnimationFrame");
});
console.log("7. [Sync] 끝");
우선 초기에는 console.log(“1. [Sync] 시작”); 부분이 콜 스택에 쌓인다.
- 콜 스택에 쌓인 이후 동기작업이므로 바로 실행되고, 콜스택에서 제거된다.
다음으로 콜스택에는 setTimeout이 쌓이고, web API를 사용하는 작업이므로 web API로 작업을 넘긴다.
브라우저에서는 0ms 뒤에 해당 콜백을 테스크 큐에 다시 넣어준다.
이후 콜스택에는 promise가 쌓이고, 해당 작업은 테스크 큐에 담긴다.
이후 콜 스택에는 requestAnimationFrame이 호출되고 브라우저의 render queue에 콜백을 넘겨준다.
이후 콜스택에는
console.log("7. 끝")이 담기고, 실행된다.이제 콜스택이 다 비워진 이후, 테스크 큐와 render queue에 쌓인 작업들이 다시 콜스택으로 이동하게 된다.
우선, 테스크 큐에서의 microtaskQueue가 먼저 콜스택으로 이동한다.
- 따라서 순차적으로 3, 4가 출력된다.
이후, microTask가 다 비워진 이후에는, 브라우저의 다음 렌더링에 맞춰 render queue에 있는 작업이 콜스택으로 이동한다.
마지막으로 macroTask가 비워지면서 콜스택으로 이동한다.
결과적으로 순서는 1-7-3-4-5-2
렌더 큐란?
- requestAnimationFrame의 콜백 함수들이 이 큐에서 관리된다.
- 처음에는 리액트 렌더링과 혼동되어서 다음 리액트 컴포넌트가 렌더링될 때로 생각을 하고 있었다. 하지만 지금 이야기하는 부분은 js를 실행하는 환경(브라우저, node.js)에서 비동기처리를 어떻게 다루느냐의 문제이고, 리액트 관련 내용은 지금까지 하나도 안나왔기 때문에 당연히 아니다.
- 여기서 말하는 렌더링이란 브라우저의 렌더링을 말한다. 주사율, 보편적으로 60hz에 맞춰 브라우저가 화면을 새로 그릴 시점이라고 판단하는 시점에 해당 render queue에 있는 작업들이 실행된다.
- 그리고, 해당 작업들이 트리거되기 전에 call stack과 microtask들이 다 비워져있는지 확인하고, 실행된다.