자바스크립트 엔진은 자바스크립트 코드를 해석하고 실행하는 인터프리터이다.
대중적으로 알려진 엔진은 구글의 V8엔진인데, 아무래도 가장 많이 사용되는 크롬에서 사용되고, Node.js
에서 사용되는 엔진이기도 하기 때문인 듯 하다.
텍스트하지만 렌더링 엔진과 마찬가지로 브라우저마다 다른 엔진을 사용한다.
자바스크립트 엔진은 메모리 힙과 호출 스택으로 이루어져 있다.
- 메모리 힙 : 변수와 객체의 메모리 할당이 발생하는 곳
- 콜 스택 : 코드가 실행될 때마다 호출 스택이 쌓이는 곳
자바스크립트 엔진에서 제공해주지 않지만 자바스크립트 개발자가 사용하는 setTimeout
과 같은 API들이 있다. 이러한 API들은 브라우저에서 제공하는 Web API이다. 그리고 이런 Web API 의 호출을 제어하기 위한 콜백큐와 이벤트 루프가 있다.
자바스크립트는 싱글 쓰레드 언어이기 때문에, 한번에 한가지 작업만 실행하게 된다.
콜 스택이란 프로그램에서 우리가 어디 있는지를 기록하는 자료 구조이다. 함수를 실행하면, 그 함수가 콜 스택의 가장 상단에 위치하게 된다. 그리고 함수의 실행이 종료 되면 그 함수는 스택에서 제거(Pop) 된다.
그리고 호출 스택의 각 항목을 스택 프레임이라고 한다.
function foo() {
foo();
}
foo();
위와 같은 함수를 호출하게 되면 콜스택 위에 계속 함수가 쌓일 것이다. 그리고 호출 스택의 실제 크기를 초과하게 되면 브라우저는 Maximum call stack size exceeded
라는 에러를 발생시키고 함수를 종료시킨다.
호출 스택에 처리 시간이 굉장히 오래 걸리는 함수가 있으면 어떻게 될까? 자바스크립트는 싱글 쓰레드 이기 때문에 함수가 종료될 때까지 다른 작업들이 모두 대기 상태가 될 것이다.
그리고 결국 브라우저는 웹 페이지를 종료할지 여부를 무든 오류메시지를 표시할 것이다.
이런 상황을 제어하기 위해 사용하는 것이 비동기 콜백이다.
즉, 코드 일부를 실행하고 나중에 실행될 콜백 함수를 제공하는 것이다. 비동기 콜백은 즉시 콜스택에 올라가는 것이 아닌, 특수한 시점에 실행이 되므로 스택안에 바로 올라가지 않는다.
그리고 이 콜백 함수들의 스케쥴을 관리하는 것이 이벤트 큐이다.
자바스크립트 런타임은 콜백 큐를 가지고 있다. 이는 처리할 메시지 목록과 실행할 콜백 함수들의 리스트이다.
setTimeout
를 포함하고 있는 함수가 실행된다고 하면, 함수는 콜스택에 올라갈 것이고, setTimeout
은 콜백 큐에 밀어 넣어진다.
이벤트 루프는 콜스택과 콜백 큐를 감시하면서, 콜스택이 비워지게 되면 콜백 큐에 대기 중인 항목이 있는지 확인하고, 있으면 스택에 올린다.
브라우저는 렌더링 도중 자바스크립트를 만나게 되면 이에 대한 해석과 실행이 완료될 때까지 렌더링을 멈춘다. 그렇기 때문에, 렌더링이 정상적으로 끝난 후 실행하기 위해 바디 하단에 스크립트를 두는게 안정적이라는 말을 한다.
하지만! 헤드 태그에서 발생할 수 있는 문제를 해결하기 위해 추가된 속성이 있다.
바로, async
와 defer
이다.
<script async src="myAsyncScript.js" onload="myInit()"></script>
<script defer src="myDeferScript.js" onload="myInit()"></script>
위와 같이 async
또는 defer
속성이 추가된 스크립트는 렌더링을 방해하지 않는다.
async
속성은 외부 스크립트에만 사용할 수 있으며, 스크립트를 내려받는 즉시 실행된다.
defer
속성은 비동기적으로 스크립트를 다운로드 하며, 렌더링이 완료된 후 스크립트를 실행한다.
https://blog.asamaru.net/2017/05/04/script-async-defer/
https://junhobaik.github.io/js-script-position/
https://new93helloworld.tistory.com/358